Saltar al contenido

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

Categoría: Seguridad / Implementación
Este artículo está disponible actualmente solo en japonés. Estamos trabajando en las traducciones.

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 <strong>validación de back-end (lado del servidor)</strong>. 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

  • <input type="checkbox" disabled> El límite de carga se define en bytes (sin confusión entre MB y MiB)
  • <input type="checkbox" disabled> Para PHP, tanto <code>upload_max_filesize</code> como <code>post_max_size</code> están configurados
  • <input type="checkbox" disabled> Para Nginx, <code>client_max_body_size</code> incluye una asignación para multipart overhead
  • <input type="checkbox" disabled> Hay manejo de errores para cuando <code>$_FILES['file']['error']</code> es UPLOAD_ERR_INI_SIZE / UPLOAD_ERR_FORM_SIZE
  • <input type="checkbox" disabled> 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)

  • <input type="checkbox" disabled> No se confía en el <code>Content-Type</code> (<code>$_FILES['file']['type']</code>) enviado desde el cliente
  • <input type="checkbox" disabled> La validación de tipo MIME del lado del servidor se realiza mediante <code>finfo</code> / <code>mime_content_type()</code>
  • <input type="checkbox" disabled> 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

  • <input type="checkbox" disabled> Se define una lista blanca de extensiones de archivo (lista blanca, no lista negra)
  • <input type="checkbox" disabled> Se detectan y rechazan las extensiones dobles (por ejemplo, <code>shell.php.jpg</code>)
  • <input type="checkbox" disabled> Se aplica normalización de mayúsculas/minúsculas durante la validación (tratando <code>.JPG</code> y <code>.jpg</code> 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)

  • <input type="checkbox" disabled> 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

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

6. Manejo de errores y respuesta

  • <input type="checkbox" disabled> Se devuelven códigos de estado HTTP apropiados (200/201) en la carga exitosa
  • <input type="checkbox" disabled> Se devuelve <strong>413 Payload Too Large</strong> cuando se excede el tamaño
  • <input type="checkbox" disabled> Se devuelve <strong>422 Unprocessable Entity</strong> para formatos de archivo no válidos
  • <input type="checkbox" disabled> 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Éxito<a href="/ja/files/threshold/">Archivos de umbral</a>
Archivo que excede el límite por 1 byteError 413<a href="/ja/files/threshold/">Archivos de umbral</a>
Archivo vacío de 0 bytesError de validaciónCreación Manual
Archivo con extensión falsa (PHP enmascarado como <code>.jpg</code>)Error MIME<a href="/ja/files/broken/">Archivos rotos</a>
Archivo con encabezado dañadoError de validación<a href="/ja/files/broken/">Archivos rotos</a>

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. <strong>Validación de tipo MIME en el servidor</strong> (uso de <code>finfo</code>) — No confíes en las declaraciones del cliente
  2. <strong>Guardar con nombre de archivo aleatorio</strong> — No use el nombre de archivo original
  3. <strong>Desabilitar ejecución de PHP en el directorio de carga</strong> — Prevenir que scripts se ejecuten en el directorio de carga

Archivos de prueba para este artículo

  • → <a href="/ja/files/broken/" class="text-primary-600 dark:text-primary-400 hover:underline">Lista de Archivos Dañados (Para Pruebas de Errores de Validación)</a>
  • → <a href="/ja/files/threshold/" class="text-primary-600 dark:text-primary-400 hover:underline">Lista de archivos de prueba de umbral (9.9MB / 10MB / 10.1MB)</a>
  • → <a href="/ja/files/images/" class="text-primary-600 dark:text-primary-400 hover:underline">Lista de archivos de prueba de imagen (PNG / JPG / WebP / GIF)</a>

Artículos relacionados

  • → <a href="/ja/blog/how-to-test-upload-limit/" class="text-primary-600 dark:text-primary-400 hover:underline">Cómo Probar Correctamente los Límites de Carga de Archivos</a>
  • → <a href="/ja/reference/magic-bytes/" class="text-primary-600 dark:text-primary-400 hover:underline">Referencia de bytes mágicos (firma de archivo)</a>