Subida de ficheros segura en PHP: lo que reviso siempre

Sergio
12 de Agosto del 2026

Con varios hilos por aquí de subida de ficheros en PHP, van los puntos que reviso siempre en un formulario de subida, porque los fallos de seguridad más comunes vienen todos de saltarse alguno de estos:

1. Nunca confiar en la extensión del nombre de fichero. Un atacante puede subir shell.php renombrado a foto.jpg con contenido PHP real dentro. Comprobar el tipo real de contenido con finfo_file(), no con pathinfo($nombre, PATHINFO_EXTENSION):

$finfo = finfo_open(FILEINFO_MIME_TYPE);
$tipo_real = finfo_file($finfo, $_FILES['archivo']['tmp_name']);
finfo_close($finfo);

$permitidos = ['image/jpeg', 'image/png', 'image/webp'];
if (!in_array($tipo_real, $permitidos, true)) {
    throw new Exception('Tipo de fichero no permitido');
}

2. Nunca guardar el fichero con el nombre que envía el usuario. Genera un nombre nuevo (por ejemplo con bin2hex(random_bytes(16))) y guarda el nombre original solo como metadato en base de datos si lo necesitas mostrar después. Evita tanto ataques de path traversal (../../etc/passwd) como colisiones de nombres.

3. El directorio de subida no debe ser ejecutable. Si usas Apache o Nginx, configura ese directorio para que sirva ficheros estáticos pero nunca ejecute PHP, ni aunque alguien consiga colar un .php disfrazado. Es la última línea de defensa cuando fallan las dos anteriores.

4. Limitar el tamaño, tanto en PHP (upload_max_filesize/post_max_size en php.ini) como comprobando $_FILES['archivo']['size'] en tu código, porque un usuario malicioso puede saltarse cualquier límite puesto solo en el HTML del formulario (el atributo max de un input, por ejemplo, no protege nada por sí solo).

¿Algún otro punto que añadiríais a la lista?

Sergio


David Carrero
12 de Agosto del 2026

Añadiría un par de cosas a la lista que seguramente se dan por hechas pero se olvidan en la práctica:

1. Nunca confiar en $_FILES['tipo']['type'] ni en la extensión del nombre del fichero, ninguno de los dos lo pone el servidor, los pone el navegador del cliente y se pueden falsificar sin ningún esfuerzo. Hay que comprobar el tipo real leyendo los primeros bytes del fichero (magic bytes) con finfo_file() en vez de fiarte de lo que dice el propio upload.

2. Si el fichero subido va a acabar sirviéndose públicamente, que el nombre final no lo elija nunca el usuario ni derive directamente del nombre original. Generar un nombre aleatorio (hash, UUID) evita tanto colisiones como la posibilidad de subir algo con un nombre tipo ../../algo.php si en algún punto no se sanea bien la ruta.

3. Si puedes, guarda los ficheros subidos fuera del directorio público del servidor web, y sírvelos a través de un script PHP que compruebe permisos antes de hacer readfile(). Así, aunque alguien consiguiera subir algo ejecutable saltándose las validaciones anteriores, no estaría accesible directamente por URL.

David