Validar correctamente la subida de un archivo con php

Cuando nos enfrentamos a esta situación se nos viene a la cabeza distintas formas de querer validar un fichero, según sea la necesidad; por tamaño, extension, etc.
Lo que voy a explicar es como realizar la una validación correcta y eficiente del tipo de archivo que se está subiendo y aprovecho de explicar tambien la importancia de realizar bien esta validación.

Antes que todo, hay que tener en claro como funciona esto de la subida de archivos y tener en mente que la validación se puede y debe hacerse a nivel de cliente y a nivel de servidor. Muchas veces me he enfrentado a situaciones donde el desarrollador solo hace la validación a nivel de cliente, en javascript. Por ejemplo, así:

  1. <script>
  2. function validar(archivo){
  3.    var b = archivo.split(‘.’);
  4.    if(b[b.length-1] == ‘txt’)
  5.       return true;
  6.    else{
  7.       alert(‘Error: El archivo debe ser .txt’);
  8.       return false;
  9.    }
  10. }</script>

Si llamamos a esa función pasando como parámetro archivo.txt no mostrara ningún error, de lo contrario, si pasamos otro argumento como pelicula.avi devolverá error y no nos dejará continuar.
Este tipo de validación es el menos eficiente, ya que realizar una petición por POST sin pasar por el formulario es algo muy simple, basta con tener las herramientas necesarias como por ejemplo curl.
Hay que dejar en claro que el hecho de validar un formulario unicamente en javascript es una gran irresponsabilidad, ya sea la validación de unos simples datos como los típicos nombres, apellidos, email hasta la subida de archivos. El realizar una validación por javascript nos entrega una capa más de seguridad, pero no la única.

Cuando realizamos un upload, para que el archivo pueda ser validado y procesado a nivel de servidor (php), es necesario que el archivo exista fisicamente en el servidor, es decir, que ya se haya subido, lo que no significa que vaya a ser publicado.

ciclo de validacion

La validación correcta para el tipo de un archivo es mediante mime-type y no mediante su extension, ya que cambiarla es muy simple. El mime-type viene impregnado en las cabeceras del archivo y php es capáz de acceder a ellas cuando el fichero se encuentra en nuestro directorio temporal (ver imágen).
Cuando ya hemos realizado el upload, antes de realizar la operación mode_uploaded_file() debemos hacer la validación por su mime:

  1. < ?php
  2. $_FILES[‘nombreDelCampo’][‘type’];
  3. ?>
  4.  

En esa variable se guardara el mimetype del fichero que ya hemos subido y que, actualmente, esta en nuestro directorio temporal. Los tipos de mimes pueden ser:

[…]
image/jpeg
image/png
image/gif
text/plain
application/octet-stream
audio/mpeg3
application/msword
[…]

Lista más completa de MimeTypes.

La importancia de realizar una validación correcta es sensilla, nos pueden meter un caballo de troya, es decir, subir un fichero que tenga extension .jpg pero que en realidad sea un .mp3. Si nos basamos solamente en la validacion javascript, será muy facil saltarnos esa validacion y subir un fichero con la extension que nosotros queramos, por ejemplo un .php “maligno” que nos permita una shell remota via web o algún fichero .exe con algún virus o troyano que queramos difundir.
Un ejemplo claro es lo que pasó en este caso, donde alguien subio a un servidor un fichero .exe infectado que luego empezo a propagarlo vía email usando un dominio supuestamente fiable.

2 comentarios

  1. Por favor me pueden explicar como cargo el nombre del archivo del servidor del tal forma que $_FILES lo reconozca como un campo tipo file.
    De antemano, gracias por la colaboración.

  2. Buenos días. Muchas gracias por compartir tus conocimientos en PHP. Se me presenta el siguiente problema, usando un código php para upload , que admite imágenes y pdf de máximo 1 MB, y que valida el mimetype tal como indicas. Algunas veces, el archivo carga distorsionado, me explico: si comparo el archivo original con el que subió al servidor, son iguales en tamaño y extensión, pero el archivo en el servidor muestra la imagen distorsionada (muestra franjas blancas o de otros colores que ocultan el contenido, o simplemente la imagen incompleta). Usé comp.exe para comparar dos de estos archivos, y aparece error de desplazamiento, es decir, corrobora que los contenidos no son iguales. La conexión del usuario no es buena, pero desde esa misma conexión me envían el mismo archivo adjunto a un correo de Gmail, por ejemplo,y llega bien, así que ese tampoco sería el problema, creo yo. He tenido en cuenta varias recomendaciones que me han hecho, sobre revisar que los parámetros de post_max_size, upload_max_size, etc. sean adecuados.
    Hice una prueba con un archivo jpg comprimido como .zip, y el resultado es el mismo, el archivo .zip es válido pero al tratar de descomprimirlo me dice que el jpg está corrupto. ¿Como puedo validar si el error está en la transmisión (desde que se guarda en el /tmp está corrupto) o en el copiado (de /tmp a la carpeta deseada, usando php)?

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Esto sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.