-
Notifications
You must be signed in to change notification settings - Fork 0
13 seguridad en la subida de archivos en php
Cuando se permite la subida de archivos a usuarios en una aplicación web, se abre la puerta a nuevas posibilidades de hackeo, pero suele ser un requisito en las aplicaciones de hoy en día. Subir archivos está disponible tanto en redes sociales como en cualquier aplicación con perfiles de usuario, blogs, foros, sitios de banca, etc. Los usuarios pueden subir imágenes, vídeos y muchos otros tipos de archivos más. Cuanta más libertad se deja al usuario más aumentan los riesgos. Aun así, un gran número de sitios web no disponen de las medidas de seguridad adecuadas.
Indice de contenido
1. Formulario simple sin validación | 5. Comprobar el header de las imágenes |
2. Validación Mime Type | 6. Proteger la carpeta de subidas con .htaccess |
3. Bloquear extensiones de archivos peligrosas | 7. Validación por el lado del cliente |
4. Extensiones dobles de archivos | 8. Medidas de prevención de ataques |
Un formulario sencillo de subida de archivos normalmente consiste en un formulario HTML y un script PHP.
<form enctype="multipart/form-data" action="upload.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="100000" />
Elige el archivo a subir: <br>
<input name="uploadedfile" type="file" /><br />
<input type="submit" value="Subir archivo" />
</form>
<?php
$target_path = "uploads/";
$target_path = $target_path . basename($_FILES['uploadedfile']['name']);
if (move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
echo "El archivo " . basename($_FILES['uploadedfile']['name']) . " se ha subido con éxito";
} else {
echo "Hubo un error subiendo el archivo, por favor inténtalo de nuevo!";
}
?>
Cuando PHP recibe el POST request con el tipo de codificación multiplart/form-data, crea un archivo temporal con un nombre aleatorio en un directorio temporal. PHP también genera el array global $_FILES con información sobre el archivo subido:
- $_FILES['uploadedfile']['name']. Nombre original del archivo en el ordenador del usuario
- $_FILES['uploadedfile']['type']. El MIME type del archivo
- $_FILES['uploadedfile']['size']. El tamaño del archivo en bytes
- $_FILES['uploadedfile']['tmp_name']. El archivo temporal guardado de archivo subido
La función move_uploaded_file() moverá el archivo temporal a una carpeta, en este caso a /uploads. En este ejemplo no existe ninguna restricción acerca de los archivos permitidos para subir, por lo que un atacante puede subir archivos PHP o .NET con código malicioso.
Un error común que cometen los desarrolladores web cuando intentan proteger las subidas es comprobar sólo el MIME Type devuelto por PHP. Cuando un archivo se sube al servidor, PHP establecerá el elemento del array $_FILES['uploadedfile']['type'] en función del MIME Type proporcionado por el navegador web que el cliente está usando. Sin embargo, la validación no puede depender sólo en este valor. Un usuario malicioso puede subir archivos fácilmente usando un script o aplicación automática que permite el envío de HTTP POST requests, que le permiten enviar un MIME Type falso.
En ocasiones el desarrollador crea una lista negra de extensiones de archivo peligrosas, y el acceso es denegado si la extensión del archivo subido está en esa lista negra.
Una desventaja de usar listas negras de extensiones de archivos es que es casi imposible tener una lista completa que incluya todas las posibles extensiones que un atacante puede usar. Si el código funciona en un entorno de hosting, normalmente éstos permiten una gran cantidad de lenguajes: Perl, Python, Ruby, etc, y la lista puede ser interminable.
Un atacante podría subir un archivo del tipo .htaccess que podría contener una línea de código como la siguiente:
AddType application/x-httpd-php.jpg
La línea anterior de dice al servidor de Apache que ejecute las imágenes .jpg como si fueran scripts PHP, por lo que ahora el atacante puede subir un archivo .jpg que contiene código PHP. Este código podría ejecutar la función phpinfo() que mostraría una gran cantidad de información de la instalación PHP y el servidor.
El caso es similar al anterior, aunque en lugar de comprobar la extensión del archivo, al desarrollador extrae la extensión buscando el carácter '.' en el archivo, y extrayendo el string después del punto.
El método usado para superar esta barrera es algo más complicado, pero todavía relativamente fácil. Apache maneja los archivos de forma que pueden tener más de una extensión, y el orden de las extensiones normalmente es irrelevante. Por ejemplo si un archivo hola.html.es indica que es del tipo text/html y su lenguaje es el español, entonces el archivo hola.es.html contendrá la misma información. Si más de una extensión es dada e indica el mismo tipo de meta información, entonces el de la derecha se usará, excepto para lenguajes y codificación de contenidos. Por ejemplo, si .gif indica el MIME type image/gif y .html indica que es text/html, entonces el archivo hola.gif.html se asociará con el MIME Type text/html.
Por lo tanto, un archivo llamado archivo.php.123 será interpretado como PHP y podrá ejecutarse. Esto sólo funciona si la última extensión .123 no está especificada en la lista de mime-types conocidos por el servidor. Los desarrolladores a veces no son conscientes de esa característica de Apache, y puede ser muy peligrosa. Un atacante puede subir un archivo hey.php.123 y evitar el control de subida. El script comprobará que .123 no está en la lista negra.
Dicho esto, es imposible predecir la cantidad de extensiones que podría subir un atacante.
Una mejor forma de protegerse frente a la subida de archivos es con una lista blanca. En este caso el desarrollador define una lista de extensiones conocidas y no permite extensiones que no estén especificadas en la lista.
Sin embargo esta opción también es vulnerable. Existen dos formas de configurar Apache para ejecutar código PHP: con la directiva AddHandler o con la directiva AddType. Si se usa la directiva AddHandler, todos los archivos que contienen la extensión .php (incluídas tambíen las dobles como .php.jpg) se ejecutarán como script PHP. Si tu configuración de Apache contiene la siguiente línea, es posible que tu script sea vulnerable:
AddHandler php5-script.php
Un atacante puede subir un archivo llamado filename.php.jpg, saltarse la protección y ejecutar el código.
Cuando sólo se permite la subida de imágenes, los desarrolladores suelen comprobar el header de la imagen mediante la función getimagesize(), que devuelve el tamaño de la imagen. Si la validación de la imagen es incorrecta (el header no es correcto), la función devuelve false. Si un atacante intenta subir un comando PHP embebido en un archivo .jpg, la función devolverá false, y no podrá subir el archivo. Sin embargo esta medida se puede saltar fácilmente: si una imagen se abre en un editor de imágenes como Gimp, se puede editar el comentario de la imagen donde se puede insertar el código PHP.
La imagen mantendrá el header, y por lo tanto pasa el control de getimagesize().
Otra forma popular de asegurar la subida de archivos es proteger la carpeta donde los archivos se suben mediante .htaccess. La idea es restringir la ejecución de archivos desde esta carpeta. Si se usa este tipo de escenario, se inserta una línea similar a ésta:
AddHandler cgi-script .php .php3 .php4 .phtml .pl .py .jsp .asp .htm .shtml .sh .cgi Options –ExecCGI
La línea anterior es otro tipo de lista negra, que no es realmente muy segura. En el propio manual de PHP, en la sección move_uploaded_file, hay un warning que dice que si el archivo de destino ya existe, será sobreescrito.
Ya que los archivos subidos podrán y serán sobreescritos con los ya existentes, un atacante puede fácilmente reemplazar el archivo .htaccess con una versión modificada. Esto le permitirá ejecutar scripts específicos que pueden comprometer la seguridad del servidor.
Otra medida común de seguridad usada en la subida de archivos es por el lado del cliente. Esta medida suele ser más común en lenguajes como ASP.NET, ya que ofrece facilidades en los controles de validación.
Este tipo de controles de validación permiten al usuario hacer comprobaciones de expresiones regulares en el archivo que se sube, para comprobar que la extensión del archivo que se sube está especificado en la lista de extensiones permitidas.
Igualmente, es franqueable, se puede escribir un script en el lado del cliente que hará la validación en lugar del script que proporciona la aplicación web. Si no usa un navegador web, el atacante puede usar una aplicación que permite enviar HTTP POST request para subir el archivo.
La siguiente lista de medidas ayuda a prevenir ataques en la subida de archivos:
- Define un archivo .htaccess que sólo permita el acceso a archivos con extensiones específicas.
- No pongas el .htaccess en la misma carpeta donde se suben los archivos, se debe situar en el directorio padre.
- Un .htaccess típico que sólo permite los archivos gif, png, jpg, jpeg y png debería incluir el siguiente código, que previene también de ataques por doble extensión de archivos:
deny from all
<Files ~ “^w+.(gif|jpe?g|png)$”>
order deny,allow
allow from all
</Files>
- Si es posible, sube los archivos en un directorio fuera del server root.
- Evita la posibilidad de sobreescritura de archivos (para evitar el ataque que sobreescribe el .htaccess).
- Crea una lista de mime-types aceptados.
- Genera un nombre de archivo aleatorio y añade la extensión previamente generada.
- No confíes sólo en una validación por el lado del cliente, porque no es suficiente. Lo ideal es implementar una seguridad tanto por el lado del cliente como por el lado del servidor.
Anterior | Siguiente |
@intelguasoft - Full Stack Developer
Versión: | 0.0.3-beta |
El curso está organizado según las temáticas indicadas en la página oficial de Rogue Wave Zend para la certificación de PHP.
Esta curso no tiene ninguna relación con Zend. |
---|---|---|
Fecha: | 28.11.2018 |
Básicos
Sintaxis básicaOperadores
Operadores bit a bit
Variables
Estructuras de control
Constantes y constructores base
Espacio de nombres
Extensiones
Configuraciones
Variables al descubierto
Recolector de basuras
Rendimiento (Performance)
Funciones
FuncionesArgumentos en funciones
Funciones variables
Valores por referencia en funciones
Funciones que devuelven algo
Ámbito de variables
Funciones anónimas y closure's
Cadenas y patrones
Las comillas y las cadenas de caracteresHeredoc y Nowdoc
Comparando cadenas de caracteres
Extracción en cadenas de caracteres
Análisis en cadenas de caracteres
Reemplazos en cadenas de caracteres
Formato de cadena de caracteres
Expresiones regulares (RegEx)
Codificación de caracteres
Codificación en cadenas de caracteres
Arreglos (Array's)
ArreglosArreglos asociativos
Iterar arreglos
Funciones de arreglos
SPL Arreglos mediante objetos
Conversión de arreglos
Archivos (I/O)
Manejo de archivosLectura de archivos
Escritura de archivos
Funciones del sistema de archivos
Socket's y archivos
Streams (?)
Seguridad
Concepto y funcionamiento CGIConfigurando la seguridad
Seguridad en Sesiones
Ataques XSS
Ataques CSRF
Ataques SQLInjection
Ataques CodeInjection
Ataques EmailInjection
Filtrado de datos de entrada
Escape de datos de salida
Encriptación y contraseñas
Seguridad en el almacenamiento de datos
Seguridad en la subida de datos
SSL y OpenSSL
Base de datos
Principios básicos SQLLos joins en SQL
Principales funciones SQL
Preparando sentencias SQL
Transacciones en SQL
Algo de PDO
Programación Orientada a Objetos
Instancias de clasesModificadores y herencia de clases
Interfaces
Excepciones
Auto-carga (Autoload)
Reflexión (Reflection)
Determinación de tipos (Type hinting)
Constantes de clase
Enlace estático de ejecución (Late Static Binding)
Métodos mágicos
Librería estándar PHP (SPL)
Generadores (Generators)
Traits
Clases abstractas
Formatos de información
Algo de XMLAlgo de SimpleXML
Algo de XML Parser
Algo de PHP DOM
Algo de Web Services
Web Services con SOAP
Algo de REST
Algo de JSON
Formato de fecha y hora
Características web's
SesionesFormularios
Métodos GET y POST
Cookies
Protocolo HTTP y sus headers
Autenticación HTTP
Códigos de estado HTTP