Entendiendo los métodos open(), verify() y load() de Pillow desde la perspectiva de la seguridad
La carga de imágenes no es simplemente “recibir un archivo de imagen”; es pasar una entrada externa a un decodificador (parser). Por eso, los tres métodos de Pillow son más importantes por cuándo se invocan (es decir, cuándo se abre la superficie de ataque) que por su descripción funcional.
open() no es una función que “levanta píxeles”
Image.open() funciona de forma perezosa. Sólo abre el archivo y lo identifica; los datos de píxeles pueden no leerse todavía. Además, el descriptor de archivo puede permanecer abierto.
En seguridad y operación, el uso correcto de open() es sencillo:
- Con
open()se obtiene información ligera como formato, ancho/alto - Se bloquea según política: formatos permitidos, resolución máxima/píxeles, límite de tamaño de carga
- Se procede a la siguiente fase (verificación/decodificación)
En otras palabras, open() debe usarse como una herramienta para extraer información de decisión antes de la decodificación.
¿Qué garantiza verify() y qué no?
verify() intenta comprobar si el archivo está dañado, pero no decodifica los datos de imagen. Si encuentra un problema, lanza una excepción y, para seguir usando la imagen, se debe volver a abrir.
Desde la perspectiva de la seguridad, las conclusiones son dos:
- Ventaja: evita la decodificación (una operación pesada) y filtra rápidamente archivos “dañados”.
- Limitación: pasar
verify()no significa “seguro”; simplemente indica que el archivo no parece estar gravemente dañado. Problemas que aparecen al final de la decodificación pueden surgir enload().
load() es peligroso si se llama indiscriminadamente en la fase de verificación
load() realmente decodifica (incluye la descompresión) y carga los píxeles en memoria. Este punto es una superficie de ataque directa a DoS (agotamiento de recursos). Un archivo que parece pequeño puede generar una gran cantidad de datos al decodificar.
Pillow advierte sobre el riesgo de “primavera de descompresión” y lanza una excepción cuando se supera un umbral predeterminado (por ejemplo, 128 Mpx). Django, por la misma razón, utiliza verify() en lugar de load() durante la validación de carga de imágenes. El código fuente incluye comentarios como “load() carga la imagen completa en memoria y es un vector DoS” y realmente llama a Image.open() seguido de verify().
En Django/DRF: llamar a verify() en ImageField puede ser redundante
La validación de ImageField en formularios de Django ya ejecuta internamente Image.open() + verify(). El serializers.ImageField de DRF delega la validación a Django con un comentario que indica que se llama a la validación de Django.
Por lo tanto, si ya usas serializers.ImageField en DRF:
- Llamar a
verify()envalidate()solo para comprobar la integridad suele ser una tarea redundante. - Si necesitas una validación de negocio o seguridad adicional, considera usar
FileFielden lugar deImageFieldy diseñar tu propio pipeline de validación, lo que hace más claro el coste y la responsabilidad.
Cómo garantizar la “seguridad” de los archivos subidos por el usuario

La respuesta más realista es la siguiente:
“No utilices el archivo original tal cual; decodifica en el servidor y guarda el resultado como un nuevo archivo.”
- Con
open()obtén información ligera (ancho/alto/formato) y bloquea según política. - Con
verify()elimina archivos claramente dañados. - Para los que pasan, decodifica en un entorno controlado y normaliza a píxeles estándar como RGB/RGBA.
- Re‑codifica el resultado en el formato elegido por el servidor y crea un nuevo archivo.
- El servicio solo almacena y sirve el archivo re‑codificado.
Esta estrategia permite que el servidor controle la forma final de la salida, eliminando metadatos innecesarios o estructuras extrañas del original.
Sin embargo, la re‑codificación implica una decodificación equivalente a load(). Por eso, primero establece límites de píxeles/memoria (para defender la primavera de descompresión) y, si es posible, ejecuta el proceso en un trabajador o proceso aislado.
Resumen
open(): “identificación + extracción de información ligera” (los píxeles pueden no estar cargados).verify(): “filtro inicial de integridad” (sin decodificación; se necesita re‑abrir para usar).load(): “punto donde comienza la decodificación y el uso de memoria” (evitar su uso indiscriminado en la fase de verificación).- Respuesta práctica para la seguridad de la carga: confía únicamente en el archivo re‑codificado por el servidor (con límites y aislamiento adecuados).
Enlaces relacionados :
- ¿Cómo se ve un archivo de imagen desde la perspectiva del desarrollador? Desglosando un archivo de imagen
- Guía de seguridad para la carga de imágenes en Django: procesar eficientemente sin que el servidor se caiga
- python‑magic: la forma más práctica de confiar en el contenido del archivo en lugar de la extensión