2011-04-24 39 views
5

Tengo un montón de contenido subido por el usuario y quiero validar que los archivos de imagen cargados no son, de hecho, secuencias de comandos maliciosas. En la documentación de Django, indica que ImageField:Validación Django ImageField (¿es suficiente)?

"Hereda todos los atributos y métodos de FileField, pero también valida que el objeto cargado sea una imagen válida."

¿Es eso totalmente exacto? He leído que comprimir o manipular un archivo de imagen es una buena prueba de validación. Supongo que PIL hace algo como esto ...

¿Imaginará ImageField un largo camino para cubrir la seguridad de carga de mi imagen?

Respuesta

1

Otra prueba es con el comando file. Comprueba la presencia de "números mágicos" en el archivo para determinar su tipo. En mi sistema, el paquete file incluye libmagic, así como un contenedor basado en los tipos /usr/lib64/python2.7/site-packages/magic.py. Parece que lo utilice como: (. Código de here)

import magic 

ms = magic.open(magic.MAGIC_NONE) 
ms.load() 
type = ms.file("/path/to/some/file") 
print type 

f = file("/path/to/some/file", "r") 
buffer = f.read(4096) 
f.close() 

type = ms.buffer(buffer) 
print type 

ms.close() 


En cuanto a su pregunta original: "Leer la Fuente, Lucas."

django/core/archivos/images.py:

""" 
Utility functions for handling images. 

Requires PIL, as you might imagine. 
""" 

from django.core.files import File 

class ImageFile(File): 
    """ 
    A mixin for use alongside django.core.files.base.File, which provides 
    additional features for dealing with images. 
    """ 
    def _get_width(self): 
     return self._get_image_dimensions()[0] 
    width = property(_get_width) 

    def _get_height(self): 
     return self._get_image_dimensions()[1] 
    height = property(_get_height) 

    def _get_image_dimensions(self): 
     if not hasattr(self, '_dimensions_cache'): 
      close = self.closed 
      self.open() 
      self._dimensions_cache = get_image_dimensions(self, close=close) 
     return self._dimensions_cache 

def get_image_dimensions(file_or_path, close=False): 
    """ 
    Returns the (width, height) of an image, given an open file or a path. Set 
    'close' to True to close the file at the end if it is initially in an open 
    state. 
    """ 
    # Try to import PIL in either of the two ways it can end up installed. 
    try: 
     from PIL import ImageFile as PILImageFile 
    except ImportError: 
     import ImageFile as PILImageFile 

    p = PILImageFile.Parser() 
    if hasattr(file_or_path, 'read'): 
     file = file_or_path 
     file_pos = file.tell() 
     file.seek(0) 
    else: 
     file = open(file_or_path, 'rb') 
     close = True 
    try: 
     while 1: 
      data = file.read(1024) 
      if not data: 
       break 
      p.feed(data) 
      if p.image: 
       return p.image.size 
     return None 
    finally: 
     if close: 
      file.close() 
     else: 
      file.seek(file_pos) 

por lo que parece que sólo lee el archivo de 1024 bytes a la vez hasta PIL dice que es una imagen, luego se detiene. Evidentemente, esto no es integridad; compruebe todo el archivo, por lo que realmente depende de lo que quiere decir con "cubrir la seguridad de carga de mi imagen": los datos ilícitos podrían adjuntarse a una imagen y pasarse a través de su sitio. Alguien podría poner a prueba su sitio al cargar una gran cantidad de basura o un archivo realmente grande. Podría ser vulnerable a un ataque de inyección si no verifica los subtítulos cargados o si hace suposiciones sobre el nombre de archivo cargado de la imagen. Y así.

+0

Hola Mike, puedo ver de qué sirve esto, pero ¿es redundante cuando se combina con ImageField? Claramente ImageField realiza algún tipo de validación de tipo de archivo – Ben

+0

Gracias por la actualización, y tiene razón acerca de las limitaciones. Tengo algunas estrategias en mente para lidiar con la prevención de grandes cargas de archivos. Las posibilidades de ataque de inyección son mis grandes preocupaciones. – Ben

7

Django valida la imagen cargada mediante el formulario mediante PIL. Ver https://code.djangoproject.com/browser/django/trunk/django/forms/fields.py#L519

try: 
    # load() is the only method that can spot a truncated JPEG, 
    # but it cannot be called sanely after verify() 
    trial_image = Image.open(file) 
    trial_image.load() 

    # Since we're about to use the file again we have to reset the 
    # file object if possible. 
    if hasattr(file, 'reset'): 
     file.reset() 

    # verify() is the only method that can spot a corrupt PNG, 
    # but it must be called immediately after the constructor 
    trial_image = Image.open(file) 
    trial_image.verify() 
... 
except Exception: # Python Imaging Library doesn't recognize it as an image 
    raise ValidationError(self.error_messages['invalid_image']) 

documentación PIL dice lo siguiente acerca de verificar():

Los intentos para determinar si el archivo se ha roto, sin tener que decodificar los datos de imagen . Si este método encuentra algún problema, plantea excepciones adecuadas. Este método solo funciona en una imagen recién abierta; si la imagen ya se ha cargado, el resultado no está definido. Además, si necesita cargar la imagen después de usar este método, debe volver a abrir el archivo de imagen .

También debe tener en cuenta que ImageField solo se valida cuando se carga con el formulario. Si guarda el modelo usted mismo (por ejemplo, utilizando algún tipo de script de descarga), la validación no se realiza.

Cuestiones relacionadas