Saltar al contenido

Lista de verificación de implementación de validación de archivos en formularios web

Categoría: Seguridad / Implementación

La implementación de la funcionalidad de carga de archivos implica numerosas consideraciones de seguridad y muchas trampas fáciles de pasar por alto. Este artículo explica una lista de verificación para implementar cargas de archivos que funcionan de manera segura en entornos de producción.

Orden recomendado de validación de archivos Orden seguro: de ligero a pesado 1. Tamaño bytes <= max NG → rechazar 2. Extensión .jpg / .png ... NG → rechazar 3. Tipo MIME finfo / mime_content_type NG → rechazar 4. Bytes mágicos FF D8 FF (JPEG) ... NG → rechazar 5. Escaneo de contenido ClamAV / VirusTotal NG → rechazar
Fig 1: Ejecutar validaciones ligeras primero y rechazar pronto

Descripción general de la lista de verificación

Esta lista de verificación se centra en la validación de back-end (lado del servidor). La validación de front-end se implementa como una mejora de UX complementaria, pero no proporciona garantías de seguridad.

1. Validación de tamaño de archivo

  • El límite de carga se define en bytes (sin confusión entre MB y MiB)
  • Para PHP, tanto upload_max_filesize como post_max_size están configurados
  • Para Nginx, client_max_body_size incluye una asignación para multipart overhead
  • Hay manejo de errores para cuando $_FILES['file']['error'] es UPLOAD_ERR_INI_SIZE / UPLOAD_ERR_FORM_SIZE
  • Hay una verificación de tamaño mínimo de archivo (excluyendo archivos de 0 bytes)
 $maxBytes) {
        throw new \RuntimeException(sprintf(
            'ファイルサイズ(%s)が上限(%s)を超えています',
            number_format($file['size']),
            number_format($maxBytes)
        ));
    }
}

2. Validación de formato de archivo (tipo MIME)

  • No se confía en el Content-Type ($_FILES['file']['type']) enviado desde el cliente
  • La validación de tipo MIME del lado del servidor se realiza mediante finfo / mime_content_type()
  • Se define una lista blanca de tipos MIME permitidos
file($file['tmp_name']);

if (!in_array($mimeType, $allowed, true)) {
    throw new \RuntimeException('許可されていないファイル形式です: ' . $mimeType);
}

3. Validación de extensión de archivo

  • Se define una lista blanca de extensiones de archivo (lista blanca, no lista negra)
  • Se detectan y rechazan las extensiones dobles (por ejemplo, shell.php.jpg)
  • Se aplica normalización de mayúsculas/minúsculas durante la validación (tratando .JPG y .jpg como iguales)
 2) {
    throw new \RuntimeException('不正なファイル名です');
}

$ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
if (!in_array($ext, $allowed, true)) {
    throw new \RuntimeException('許可されていない拡張子です: ' . $ext);
}

4. Validación de byte mágico (firma de archivo)

  • Los bytes mágicos se validan para archivos críticos (por ejemplo, excluyendo archivos ejecutables)

5. Manejo seguro del destino de guardado y nombre de archivo

  • El directorio de destino está fuera de la raíz web (o controlado mediante XSendFile/X-Accel-Redirect)
  • Los nombres de archivo guardados se generan aleatoriamente (por ejemplo, UUID) y no son el nombre de archivo original
  • La traversal de ruta (por ejemplo, ../../../etc/passwd) se elimina mediante validación
  • El directorio de destino no tiene permiso de ejecución de PHP (procesamiento de PHP deshabilitado via .htaccess o configuración de Nginx)

6. Manejo de errores y respuesta

  • Se devuelven códigos de estado HTTP apropiados (200/201) en la carga exitosa
  • Se devuelve 413 Payload Too Large cuando se excede el tamaño
  • Se devuelve 422 Unprocessable Entity para formatos de archivo no válidos
  • Los mensajes de error no contienen información interna del servidor (rutas, versiones, etc.)

7. Casos de prueba

Después de la implementación, ejecute los siguientes casos de prueba para verificar el comportamiento. Puede utilizar los archivos de prueba disponibles en DevLab.

Casos de pruebaResultado esperadoArchivos a usar
Archivo exactamente en el límiteÉxitoArchivos de umbral
Archivo que excede el límite por 1 byteError 413Archivos de umbral
Archivo vacío de 0 bytesError de validaciónCreación Manual
Archivo con extensión falsa (PHP enmascarado como .jpg)Error MIMEArchivos rotos
Archivo con encabezado dañadoError de validaciónArchivos rotos

Resumen

La implementación de cargas de archivos seguras requiere validación en múltiples capas. En particular, asegúrese de implementar los siguientes tres puntos.

  1. Validación de tipo MIME en el servidor (uso de finfo) — No confíes en las declaraciones del cliente
  2. Guardar con nombre de archivo aleatorio — No use el nombre de archivo original
  3. Desabilitar ejecución de PHP en el directorio de carga — Prevenir que scripts se ejecuten en el directorio de carga

❓ Preguntas frecuentes

¿Qué debe hacer como mínimo la validación de subidas?
Tres cosas como mínimo: validar el tipo MIME en el servidor con finfo en vez de fiarte de lo que declara el cliente; guardar con un nombre aleatorio en lugar del original; y desactivar la ejecución de PHP en el directorio de subidas para que nada de lo que caiga allí pueda ejecutarse.
¿Por qué no basta con comprobar la extensión?
Las extensiones se falsifican sin esfuerzo, así que no detectarán un archivo malicioso con doble extensión como shell.php.jpg. Combina la lista blanca de extensiones con la validación MIME de finfo y una comprobación de magic bytes.
¿Qué códigos de estado HTTP son apropiados para las subidas?
Devuelve 200 o 201 si va bien, 413 Payload Too Large cuando el archivo supera el límite y 422 Unprocessable Entity para un formato inválido. No incluyas datos internos del servidor —rutas, versiones— en el mensaje de error.