Prompt y contexto
Los usuarios cargan archivos ZIP o TAR.GZ para escaneo de virus en segundo plano, extracción de texto o importación por lotes. Un atacante podría cargar un archivo comprimido diminuto que se expande enormemente, archivos anidados recursivos o rutas de entradas que escapan del directorio de destino. Explica los límites de carga, el análisis de formatos, los presupuestos de recursos, el aislamiento, los estados de fallo y la limpieza.
El enfoque es la seguridad de los recursos durante la extracción. Un límite en el tamaño de carga no es la defensa completa; la relación de compresión, el recuento de entradas y la salida descomprimida deben formar parte del diseño.
Qué evalúa el entrevistador
El entrevistador espera una distinción entre los bytes comprimidos y el costo de extracción. Incluye presupuestos de archivos, profundidad de directorios, anidamiento, CPU, memoria, disco y tiempo de reloj (wall-clock). OWASP recomienda considerar el tamaño después de la descompresión; la OWASP WSTG describe una zip bomb como un archivo que agota el disco o la memoria para causar una denegación de servicio.
Las respuestas sólidas cubren traversal, symlinks, nombres duplicados, declaraciones de tamaño falsas, vulnerabilidades del parser y limpieza tras una cancelación. Un worker aislado, un directorio por tarea, publicación atómica y telemetría mantienen la entrada maliciosa dentro de una tarea acotada.
Respuesta de 30 segundos
“Trataría cada archivo comprimido como no confiable. El edge limita los bytes comprimidos, el formato, la cuota y la tasa de peticiones. Un worker en un contenedor restringido transmite las entradas hacia un directorio temporal por tarea mientras aplica políticas de rutas de entrada, symlinks, recuento de entradas, profundidad de anidamiento y un presupuesto de bytes descomprimidos. También cuenta con límites de CPU, memoria, disco y tiempo de reloj. Cualquier límite detiene la extracción y realiza la limpieza. Solo los resultados escaneados y con tipo verificado se publican de forma atómica. Las respuestas exponen un estado seguro, mientras que los eventos de auditoría conservan códigos de motivo y uso sin el contenido sin procesar.”
Diseño paso a paso
Paso 1: Establecer límites de entrada y de tarea
Limita el tamaño comprimido, la concurrencia por usuario, la cuota total y la tasa de peticiones. Asigna un ID de tarea, tenant, objeto de origen, formato y estado. Almacena el original en un almacenamiento aislado no ejecutable; un worker recibe acceso de lectura de corta duración y nunca trata los datos cargados como código o configuración.
Paso 2: Identificar formatos y elegir parsers
No confíes únicamente en una extensión o en Content-Type; inspecciona los magic bytes y una lista de permitidos. Elige bibliotecas con mantenimiento para ZIP, TAR y GZIP y fija sus versiones. Los metadatos del tamaño de las entradas ayudan en una comprobación previa, pero no pueden ser la única autoridad; continúa contando los bytes durante la lectura. Los archivos cifrados, los métodos desconocidos y los encabezados malformados deben rechazarse o revisarse.
Paso 3: Aplicar presupuestos multidimensionales
Define límites para bytes comprimidos, bytes descomprimidos, recuento de archivos, tamaño de archivo individual, profundidad de directorios, profundidad de archivos anidados, tiempo de CPU, memoria, disco y tiempo de reloj. Cada tarea tiene un límite estricto. La relación de compresión puede activar una advertencia, pero no puede reemplazar el conteo de bytes descomprimidos porque los formatos y los datos se comprimen de manera diferente.
Paso 4: Proteger rutas y objetos del sistema de archivos
Normaliza el nombre de cada entrada y rechaza rutas absolutas, traversal y bytes nulos. Extrae dentro de un directorio exclusivo para la tarea y verifica que cada ruta final permanezca dentro de él. Rechaza symlinks, hard links y archivos de dispositivo por defecto. Define una política para nombres duplicados, usualmente el rechazo, para evitar sorpresas en el orden de sobreescritura.
Paso 5: Aislar la extracción y el escaneo
Ejecuta el worker en un contenedor o sandbox de bajos privilegios con una raíz de solo lectura, cuota de disco temporal y sin acceso a la red o con acceso mínimo. Asigna presupuestos separados a la extracción y al escaneo para que un solo archivo comprimido no pueda consumir ambos silenciosamente. Escribe los resultados del escaneo, texto y miniaturas como nuevos objetos no ejecutables, no directamente como descargas del navegador.
Paso 6: Acotar anidamiento, recursión y cancelación
Rechaza los archivos anidados cuando el producto no los necesite. Si se requiere anidamiento, establece una profundidad máxima, un presupuesto total compartido y comprobaciones de ruta en cada capa; la recursión no debe recibir una nueva cuota. Los tiempos de espera (timeouts), la cancelación, las caídas del worker y la eliminación de tenants activan una limpieza idempotente para que no queden archivos temporales.
Paso 7: Usar una máquina de estados para la publicación y recuperación
Los estados pueden ser uploaded, inspecting, extracting, scanning, published, rejected y cleanup_failed. Publica de forma atómica solo después de que cada entrada supere las comprobaciones y el escaneo. El estado seguro para el usuario puede incluir la siguiente acción; los eventos internos conservan códigos de rechazo estructurados, uso de presupuesto y errores de biblioteca. Reintentar no debe omitir presupuestos ni publicar dos veces.
Paso 8: Probar y observar los límites
Prueba relaciones de compresión elevadas, archivos anidados, traversal, symlinks, nombres duplicados, encabezados malformados, entradas gigantescas, CRCs incorrectos, archivos cifrados y cancelaciones a mitad de tarea. Mide los bytes descomprimidos, el recuento de archivos, la profundidad máxima, la CPU, el motivo de rechazo, la latencia de limpieza, el nivel de uso (waterline) del disco temporal y los reinicios del worker. Utiliza muestras maliciosas sintéticas contra la versión exacta de la biblioteca en producción.
Compensaciones, límites y ganancia de información
La comprobación previa de metadatos es económica pero no puede reemplazar los contadores en streaming; el streaming puro es más seguro pero puede consumir algunos recursos antes de descubrir un límite. Rechazar el anidamiento es lo más seguro, mientras que admitirlo sirve a flujos de trabajo de copias de seguridad a costa de presupuestos compartidos y comprobaciones recursivas.
Un worker aislado reduce el riesgo para el servicio principal pero añade latencia de cola y operaciones. Conservar los originales ayuda en la investigación y reintentos, pero requiere límites de retención, controles de acceso y cifrado. Una cola asíncrona no es una razón para permitir extracciones sin límites.
Respuesta modelo de alta calidad
“Dividiría la carga y la extracción en dos límites de confianza. El edge limita los bytes comprimidos, la concurrencia y la cuota. Un worker asíncrono en un contenedor sin red y con bajos privilegios utiliza un parser con mantenimiento y escribe solo en un directorio de tarea. A medida que lee cada entrada, cuenta los bytes descomprimidos y los archivos, y aplica presupuestos de tamaño de archivo individual, profundidad de directorios, profundidad de anidamiento, CPU, memoria, disco y tiempo de reloj.
Las rutas normalizadas rechazan rutas absolutas, traversal, symlinks, hard links y archivos de dispositivo. Si se necesita anidamiento, cada capa comparte un único presupuesto. Los resultados se escanean antes de la publicación atómica; el exceso de límites, los timeouts, la cancelación y las caídas utilizan una limpieza idempotente. La telemetría registra el uso del presupuesto, los motivos de rechazo, la latencia de limpieza y el waterline del disco, incluyendo archivos maliciosos en las pruebas de regresión.”
Errores comunes
- Limitar solo el tamaño de carga comprimido. Un archivo diminuto puede agotar el disco o la memoria después de la extracción.
- Confiar en el tamaño descomprimido declarado. Los encabezados pueden estar incompletos o no ser confiables.
- Usar la relación de compresión como la única regla. Es una señal, no un presupuesto de seguridad universal.
- Extraer en un directorio compartido. El traversal, la sobreescritura y los residuos cruzan los límites entre tenants.
- Permitir symlinks o archivos de dispositivo. Las entradas pueden redirigir escrituras fuera del destino o hacia interfaces especiales.
- Dar una nueva cuota a cada capa de anidamiento. La recursión multiplica el consumo de recursos.
- Publicar contenido sin procesar después de escanear. Los scripts y los tipos de contenido incorrectos aún pueden atacar a quienes descargan.
- Omitir la limpieza después de una cancelación. Los residuos pueden provocar un incidente de agotamiento de disco.
Preguntas de seguimiento y respuestas
¿El escaneo debería realizarse antes de la extracción?
El archivo comprimido debe leerse para escanear los archivos internos, pero la extracción en sí misma necesita aislamiento y presupuestos. Realiza primero las comprobaciones de formato y metadatos, transmite la extracción en un worker restringido y luego escanea cada resultado.
¿Con qué relación de compresión se debería rechazar?
No existe un número universal para todos los formatos. Utiliza la relación para alertas o rechazo temprano, mientras que los límites estrictos cubren bytes descomprimidos, un archivo individual, recuento de archivos, CPU, disco y tiempo de reloj.
¿Cómo se pueden admitir archivos anidados de forma segura?
Establece una profundidad máxima, comparte un presupuesto total entre las capas, repite las comprobaciones de rutas y enlaces, y mantén la recursión dentro de la cuota de tiempo y disco del mismo worker.
¿Se puede confiar en los tamaños de entrada reportados por el parser?
Son señales de advertencia, no una autorización. Continúa contando los bytes mientras lees y ten en cuenta Zip64, encabezados malformados, descriptores de datos y limitaciones conocidas de la biblioteca.
¿Se debe reintentar una tarea que excedió el límite?
Una infracción de presupuesto es un rechazo determinista y no debe reintentarse a ciegas. Reintenta únicamente fallos transitorios del worker, creando un nuevo directorio aislado con el mismo presupuesto y estado idempotente.