temas plantillas para modulos gratis descargar php image security file-upload upload

php - plantillas - prestashop gratis



Script de carga de imagen completamente seguro (2)

No sé si esto va a suceder, pero lo intentaré.

Durante la última hora hice una investigación sobre la seguridad de carga de imágenes. Aprendí que hay muchas funciones para probar la carga.

En mi proyecto, necesito estar seguro con las imágenes cargadas. También puede haber una gran cantidad y puede requerir mucho ancho de banda, por lo que comprar una API no es una opción.

Así que decidí obtener un script PHP completo para cargar una imagen REALMENTE segura. También creo que ayudará a muchas personas, porque es imposible encontrar uno realmente seguro. Pero no soy experto en php, por lo que es realmente dolor de cabeza para mí agregar algunas funciones, por lo que pediré esta ayuda de la comunidad para crear un script completo de carga de imágenes REALMENTE segura.

Los temas realmente geniales sobre eso están aquí (sin embargo, solo están diciendo lo que se necesita para hacer el truco, pero no cómo hacerlo, y como dije, no soy un maestro en PHP, así que no puedo hacer todo esto por mí mismo): lista de verificación de seguridad de carga de imágenes PHP https://security.stackexchange.com/questions/32852/risks-of-a-php-image-upload-form

En resumen, están diciendo que esto es lo que se necesita para cargar imágenes de seguridad (citaré en las páginas anteriores):

  • Inhabilite PHP para que se ejecute dentro de la carpeta de carga usando .httaccess.
  • No permita la carga si el nombre del archivo contiene la cadena "php".
  • Permitir solo extensiones: jpg, jpeg, gif y png.
  • Permitir solo el tipo de archivo de imagen.
  • No permitir la imagen con dos tipos de archivo.
  • Cambia el nombre de la imagen. Cargue en un subdirectorio, no en el directorio raíz.

También:

  • Vuelva a procesar la imagen usando GD (o Imagick) y guarde la imagen procesada. Todos los demás son divertidos y aburridos para los hackers "
  • Como señaló rr, use move_uploaded_file () para cualquier carga "
  • Por cierto, querrás ser muy restrictivo con tu carpeta de carga. Esos lugares son uno de los rincones oscuros donde muchas hazañas
    ocurrir. Esto es válido para cualquier tipo de carga y cualquier programación
    idioma / servidor. Comprobar
    https://www.owasp.org/index.php/Unrestricted_File_Upload
  • Nivel 1: Verifique la extensión (el archivo de extensión termina con)
  • Nivel 2: Verifique el tipo MIME ($ file_info = getimagesize ($ _ FILES [''image_file'']; $ file_mime = $ file_info [''mime''];)
  • Nivel 3: Lea los primeros 100 bytes y verifique si tienen bytes en el siguiente rango: ASCII 0-8, 12-31 (decimal).
  • Nivel 4: Verifique los números mágicos en el encabezado (primeros 10-20 bytes del archivo). Puede encontrar algunos de los bytes del encabezado de los archivos desde aquí:
    http://en.wikipedia.org/wiki/Magic_number_%28programming%29#Examples
  • Es posible que también desee ejecutar "is_uploaded_file" en $ _FILES [''my_files''] [''tmp_name'']. Ver
    http://php.net/manual/en/function.is-uploaded-file.php

Aquí hay una gran parte, pero aún así no es todo. (Si sabe algo más que podría ayudar a que la carga sea aún más segura, compártala).

Esto es lo que tenemos ahora

  • PHP principal:

    function uploadFile ($file_field = null, $check_image = false, $random_name = false) { //Config Section //Set file upload path $path = ''uploads/''; //with trailing slash //Set max file size in bytes $max_size = 1000000; //Set default file extension whitelist $whitelist_ext = array(''jpeg'',''jpg'',''png'',''gif''); //Set default file type whitelist $whitelist_type = array(''image/jpeg'', ''image/jpg'', ''image/png'',''image/gif''); //The Validation // Create an array to hold any output $out = array(''error''=>null); if (!$file_field) { $out[''error''][] = "Please specify a valid form field name"; } if (!$path) { $out[''error''][] = "Please specify a valid upload path"; } if (count($out[''error''])>0) { return $out; } //Make sure that there is a file if((!empty($_FILES[$file_field])) && ($_FILES[$file_field][''error''] == 0)) { // Get filename $file_info = pathinfo($_FILES[$file_field][''name'']); $name = $file_info[''filename'']; $ext = $file_info[''extension'']; //Check file has the right extension if (!in_array($ext, $whitelist_ext)) { $out[''error''][] = "Invalid file Extension"; } //Check that the file is of the right type if (!in_array($_FILES[$file_field]["type"], $whitelist_type)) { $out[''error''][] = "Invalid file Type"; } //Check that the file is not too big if ($_FILES[$file_field]["size"] > $max_size) { $out[''error''][] = "File is too big"; } //If $check image is set as true if ($check_image) { if (!getimagesize($_FILES[$file_field][''tmp_name''])) { $out[''error''][] = "Uploaded file is not a valid image"; } } //Create full filename including path if ($random_name) { // Generate random filename $tmp = str_replace(array(''.'','' ''), array('''',''''), microtime()); if (!$tmp || $tmp == '''') { $out[''error''][] = "File must have a name"; } $newname = $tmp.''.''.$ext; } else { $newname = $name.''.''.$ext; } //Check if file already exists on server if (file_exists($path.$newname)) { $out[''error''][] = "A file with this name already exists"; } if (count($out[''error''])>0) { //The file has not correctly validated return $out; } if (move_uploaded_file($_FILES[$file_field][''tmp_name''], $path.$newname)) { //Success $out[''filepath''] = $path; $out[''filename''] = $newname; return $out; } else { $out[''error''][] = "Server Error!"; } } else { $out[''error''][] = "No file uploaded"; return $out; } } if (isset($_POST[''submit''])) { $file = uploadFile(''file'', true, true); if (is_array($file[''error''])) { $message = ''''; foreach ($file[''error''] as $msg) { $message .= ''<p>''.$msg.''</p>''; } } else { $message = "File uploaded successfully".$newname; } echo $message; }

  • Y la forma:

    <form action="<?php echo $_SERVER[''PHP_SELF'']; ?>" method="post" enctype="multipart/form-data" name="form1" id="form1"> <input name="file" type="file" id="imagee" /> <input name="submit" type="submit" value="Upload" /> </form>

Entonces, lo que pido es que ayuden publicando fragmentos de códigos que me ayudarán a mí (y a todos los demás) a hacer este Script de carga de imágenes para que sea súper seguro. O compartiendo / creando un script completo con todos los fragmentos agregados.


Cargar archivos en PHP es fácil y seguro. Yo recomendaría aprender sobre:

Para cargar un archivo en PHP tiene dos métodos: PUT y POST . Para usar el método POST con HTML, debe habilitar enctype en su formulario de esta manera:

<form action="" method="post" enctype="multipart/form-data"> <input type="file" name="file"> <input type="submit" value="Upload"> </form>

Luego, en su PHP, necesita obtener su archivo cargado con $_FILES como este:

$_FILES[''file'']

Luego debe mover el archivo desde temp ("upload") con move_uploaded_file :

if (move_uploaded_file($_FILES[''file''][''tmp_name''], YOUR_PATH)) { // ... }

Y después de cargar el archivo, debe verificar la extensión del archivo. La mejor manera de hacer esto es usar pathinfo esta manera:

$extension = pathinfo($_FILES[''file''][''tmp_name''], PATHINFO_EXTENSION);

Pero la extensión no es segura porque puede cargar un archivo con extensión .jpg pero con mimetype text/php y esta es una puerta trasera. Por lo tanto, recomiendo verificar el tipo finfo_open real con finfo_open como este:

$mime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $_FILES[''file''][''tmp_name'']);

Y no use $_FILES[''file''][''type''] porque a veces, dependiendo de su navegador y sistema operativo cliente, puede recibir application/octet-stream y este mimetype no es el mimetype real de su archivo cargado.

Creo que puedes subir archivos de forma segura con este escenario.

Perdón por mi inglés, ¡chao!


Cuando empiezas a trabajar en un script de carga de imágenes seguro, hay muchas cosas a tener en cuenta. Ahora no estoy cerca de ser un experto en esto, pero me han pedido que lo desarrolle una vez en el pasado. Recorreré todo el proceso por el que he pasado aquí para que puedas seguirme. Para esto, comenzaré con un formulario html muy básico y un script php que maneja los archivos.

Formulario HTML:

<form name="upload" action="upload.php" method="POST" enctype="multipart/form-data"> Select image to upload: <input type="file" name="image"> <input type="submit" name="upload" value="upload"> </form>

Archivo PHP:

<?php $uploaddir = ''uploads/''; $uploadfile = $uploaddir . basename($_FILES[''image''][''name'']); if (move_uploaded_file($_FILES[''image''][''tmp_name''], $uploadfile)) { echo "Image succesfully uploaded."; } else { echo "Image uploading failed."; } ?>

Primer problema: tipos de archivo
Los atacantes no tienen que usar el formulario en su sitio web para cargar archivos a su servidor. Las solicitudes POST se pueden interceptar de varias maneras. Piense en complementos del navegador, servidores proxy, scripts de Perl. No importa cuánto lo intentemos, no podemos evitar que un atacante intente cargar algo (s) que no debe. Por lo tanto, toda nuestra seguridad debe hacerse en el servidor.

El primer problema son los tipos de archivo. En la secuencia de comandos anterior, un atacante podría cargar cualquier cosa que desee, como una secuencia de comandos php, por ejemplo, y seguir un enlace directo para ejecutarla. Para evitar esto, implementamos la verificación de tipo de contenido :

<?php if($_FILES[''image''][''type''] != "image/png") { echo "Only PNG images are allowed!"; exit; } $uploaddir = ''uploads/''; $uploadfile = $uploaddir . basename($_FILES[''image''][''name'']); if (move_uploaded_file($_FILES[''image''][''tmp_name''], $uploadfile)) { echo "Image succesfully uploaded."; } else { echo "Image uploading failed."; } ?>

Lamentablemente esto no es suficiente. Como mencioné antes, el atacante tiene control total sobre la solicitud. Nada impedirá que él / ella modifique los encabezados de solicitud y simplemente cambie el tipo de Contenido a "image / png". Entonces, en lugar de confiar solo en el encabezado de tipo de contenido, sería mejor validar también el contenido del archivo cargado. Aquí es donde la biblioteca php GD es útil. Usando getimagesize() , procesaremos la imagen con la biblioteca GD. Si no es una imagen, esto fallará y, por lo tanto, la carga completa fallará:

<?php $verifyimg = getimagesize($_FILES[''image''][''tmp_name'']); if($verifyimg[''mime''] != ''image/png'') { echo "Only PNG images are allowed!"; exit; } $uploaddir = ''uploads/''; $uploadfile = $uploaddir . basename($_FILES[''image''][''name'']); if (move_uploaded_file($_FILES[''image''][''tmp_name''], $uploadfile)) { echo "Image succesfully uploaded."; } else { echo "Image uploading failed."; } ?>

Sin embargo, todavía no estamos allí. La mayoría de los tipos de archivos de imágenes permiten agregarles comentarios de texto. Una vez más, nada impide que el atacante agregue algún código php como comentario. La biblioteca GD evaluará esto como una imagen perfectamente válida. El intérprete PHP ignoraría por completo la imagen y ejecutaría el código php en el comentario. Es cierto que depende de la configuración de php qué extensiones de archivo son procesadas por el intérprete de php y cuáles no, pero dado que hay muchos desarrolladores que no tienen control sobre esta configuración debido al uso de un VPS, no podemos suponer El intérprete php no procesará la imagen. Es por eso que agregar una lista blanca de extensión de archivo tampoco es lo suficientemente seguro.

La solución a esto sería almacenar las imágenes en una ubicación donde un atacante no pueda acceder al archivo directamente. Esto podría estar fuera de la raíz del documento o en un directorio protegido por un archivo .htaccess:

order deny,allow deny from all allow from 127.0.0.1

Editar: después de hablar con otros programadores de PHP, sugiero usar una carpeta fuera de la raíz del documento, porque htaccess no siempre es confiable.

Sin embargo, todavía necesitamos que el usuario o cualquier otro visitante pueda ver la imagen. Entonces usaremos php para recuperar la imagen para ellos:

<?php $uploaddir = ''uploads/''; $name = $_GET[''name'']; // Assuming the file name is in the URL for this example readfile($uploaddir.$name); ?>

Segundo problema: ataques de inclusión de archivos locales
Aunque nuestro script es razonablemente seguro en este momento, no podemos asumir que el servidor no sufra otras vulnerabilidades. Una vulnerabilidad de seguridad común se conoce como inclusión de archivo local . Para explicar esto, necesito agregar un código de ejemplo:

<?php if(isset($_COOKIE[''lang''])) { $lang = $_COOKIE[''lang'']; } elseif (isset($_GET[''lang''])) { $lang = $_GET[''lang'']; } else { $lang = ''english''; } include("language/$lang.php"); ?>

En este ejemplo, estamos hablando de un sitio web multilingüe. El lenguaje de los sitios no se considera información de "alto riesgo". Intentamos obtener el idioma preferido de los visitantes a través de una cookie o una solicitud GET e incluimos el archivo requerido basado en él. Ahora considere lo que sucederá cuando el atacante ingrese la siguiente URL:

www.example.com/index.php?lang=../uploads/my_evil_image.jpg

PHP incluirá el archivo cargado por el atacante sin pasar por el hecho de que no puede acceder al archivo directamente y estamos de vuelta en el punto de partida.

La solución a este problema es asegurarse de que el usuario no conozca el nombre del archivo en el servidor. En cambio, cambiaremos el nombre del archivo e incluso la extensión usando una base de datos para realizar un seguimiento:

CREATE TABLE `uploads` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(64) NOT NULL, `original_name` VARCHAR(64) NOT NULL, `mime_type` VARCHAR(20) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;


<?php if(!empty($_POST[''upload'']) && !empty($_FILES[''image'']) && $_FILES[''image''][''error''] == 0)) { $uploaddir = ''uploads/''; /* Generates random filename and extension */ function tempnam_sfx($path, $suffix){ do { $file = $path."/".mt_rand().$suffix; $fp = @fopen($file, ''x''); } while(!$fp); fclose($fp); return $file; } /* Process image with GD library */ $verifyimg = getimagesize($_FILES[''image''][''tmp_name'']); /* Make sure the MIME type is an image */ $pattern = "#^(image/)[^/s/n<]+$#i"; if(!preg_match($pattern, $verifyimg[''mime'']){ die("Only image files are allowed!"); } /* Rename both the image and the extension */ $uploadfile = tempnam_sfx($uploaddir, ".tmp"); /* Upload the file to a secure directory with the new name and extension */ if (move_uploaded_file($_FILES[''image''][''tmp_name''], $uploadfile)) { /* Setup a database connection with PDO */ $dbhost = "localhost"; $dbuser = ""; $dbpass = ""; $dbname = ""; // Set DSN $dsn = ''mysql:host=''.$dbhost.'';dbname=''.$dbname; // Set options $options = array( PDO::ATTR_PERSISTENT => true, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ); try { $db = new PDO($dsn, $dbuser, $dbpass, $options); } catch(PDOException $e){ die("Error!: " . $e->getMessage()); } /* Setup query */ $query = ''INSERT INTO uploads (name, original_name, mime_type) VALUES (:name, :oriname, :mime)''; /* Prepare query */ $db->prepare($query); /* Bind parameters */ $db->bindParam('':name'', basename($uploadfile)); $db->bindParam('':oriname'', basename($_FILES[''image''][''name''])); $db->bindParam('':mime'', $_FILES[''image''][''type'']); /* Execute query */ try { $db->execute(); } catch(PDOException $e){ // Remove the uploaded file unlink($uploadfile); die("Error!: " . $e->getMessage()); } } else { die("Image upload failed!"); } } ?>

Así que ahora hemos hecho lo siguiente:

  • Hemos creado un lugar seguro para guardar las imágenes.
  • Hemos procesado la imagen con la biblioteca GD
  • Hemos comprobado el tipo de imagen MIME
  • Cambiamos el nombre del archivo y cambiamos la extensión.
  • Hemos guardado tanto el nombre de archivo nuevo como el original en nuestra base de datos
  • También hemos guardado el tipo MIME en nuestra base de datos.

Todavía necesitamos poder mostrar la imagen a los visitantes. Simplemente usamos la columna de identificación de nuestra base de datos para hacer esto:

<?php $uploaddir = ''uploads/''; $id = 1; /* Setup a database connection with PDO */ $dbhost = "localhost"; $dbuser = ""; $dbpass = ""; $dbname = ""; // Set DSN $dsn = ''mysql:host=''.$dbhost.'';dbname=''.$dbname; // Set options $options = array( PDO::ATTR_PERSISTENT => true, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ); try { $db = new PDO($dsn, $dbuser, $dbpass, $options); } catch(PDOException $e){ die("Error!: " . $e->getMessage()); } /* Setup query */ $query = ''SELECT name, original_name, mime_type FROM uploads WHERE id=:id''; /* Prepare query */ $db->prepare($query); /* Bind parameters */ $db->bindParam('':id'', $id); /* Execute query */ try { $db->execute(); $result = $db->fetch(PDO::FETCH_ASSOC); } catch(PDOException $e){ die("Error!: " . $e->getMessage()); } /* Get the original filename */ $newfile = $result[''original_name'']; /* Send headers and file to visitor */ header(''Content-Description: File Transfer''); header(''Content-Disposition: attachment; filename=''.basename($newfile)); header(''Expires: 0''); header(''Cache-Control: must-revalidate''); header(''Pragma: public''); header(''Content-Length: '' . filesize($uploaddir.$result[''name''])); header("Content-Type: " . $result[''mime_type'']); readfile($uploaddir.$result[''name'']); ?>

Gracias a este script, el visitante podrá ver la imagen o descargarla con su nombre de archivo original. Sin embargo, no puede acceder al archivo en su servidor directamente ni podrá engañar a su servidor para que acceda al archivo por él / ella ya que no tiene forma de saber qué archivo es . (S) tampoco puede forzar de forma bruta su directorio de carga, ya que simplemente no permite que nadie acceda al directorio excepto el servidor mismo.

Y eso concluye mi script de carga segura de imágenes.

Me gustaría agregar que no incluí un tamaño máximo de archivo en esta secuencia de comandos, pero usted debería poder hacerlo fácilmente.

Clase ImageUpload
Debido a la gran demanda de este script, he escrito una clase ImageUpload que debería hacer que sea mucho más fácil para todos ustedes manejar de manera segura las imágenes cargadas por los visitantes de su sitio web. La clase puede manejar archivos individuales y múltiples a la vez, y le proporciona características adicionales como mostrar, descargar y eliminar imágenes.

Como el código es simplemente demasiado grande para publicarlo aquí, puede descargar la clase de MEGA aquí:

Descargar ImageUpload Class

Simplemente lea el archivo README.txt y siga las instrucciones.

Yendo a código abierto
El proyecto de clase Image Secure ahora también está disponible en mi perfil de Github . Esto para que otros (¿tú?) Puedan contribuir al proyecto y hacer de esta una gran biblioteca para todos. (actualmente con errores. Utilice la descarga anterior hasta que se solucione).