We already know how to upload an Image in Django and how to compress the image before storing it. In this article, we will see how to validate an image before processing it.
Whenever a web application or website accepts the user input in any form, input must be validated and sanitised before processing or storing it in database. An attacker might upload malicious image which might be used to compromise the system.
Let's say, we want to accept only JPEG and PNG images. Also we do not want images of size larger than 1 MB. Define these properties in settings.py file.
WHITELISTED_IMAGE_TYPES = { 'jpeg': 'image/jpeg', 'jpg': 'image/jpeg', 'png': 'image/png' } UPLOAD_FILE_MAX_SIZE = 1048576 # bytes - approx 1 mb
We must perform below mentioned 4 check on the image.
(1) Size
Check the size of the image. Return 400 BAD_REQUEST response if size of the uploaded image is more than the specified limit.
import json from django.conf import settings headers = {'content_type': 'application/json'} def upload_image(request): files = request.FILES images = files['images'] # print(images.size) # prints size in bytes if images.size > settings.UPLOAD_FILE_MAX_SIZE: return HttpResponse(json.dumps({'message': 'size {} larger than 1 MB'.format(images.size)}), headers=headers, status=400)
image.size returns the size of image in bytes. Here this image size is being compared against a constant value, of 1MB converted to bytes, defined in settings.py file.
(2) Extension
Check the extension of image. If the extension in not what is expected by application, return 400 response.
extension = images.name.split('.')[-1] if not extension or extension.lower() not in settings.WHITELISTED_IMAGE_TYPES.keys(): return HttpResponse(json.dumps({'message': 'invalid image extension'}), headers=headers, status=400)
(3) Content type:
Image have an attribute of content-type. Check its value against the whitelisted values.
content_type = images.content_type # print(content_type) if content_type not in settings.WHITELISTED_IMAGE_TYPES.values(): return HttpResponse(json.dumps({'message': 'invalid image content-type'}), headers=headers, status=400)
And now the most important check, mime-type. Extension and content-type can be spoofed by a malicious user. Hence relying only on extension and content-type check could be disastrous.
(4) Mime-type
To check the mime-type of a file, we need to read first few bytes of it. We will be doing this using the python-magic package. Please install this package before proceeding further.
pip install python-magic
In addition to checking if mime-type is among the one whitelisted, we also check if the content-type and mime-type matches or not.
import magic # check mime-type mime_type = magic.from_buffer(images.read(1024), mime=True) # print(mime_type) if mime_type not in settings.WHITELISTED_IMAGE_TYPES.values() and mime_type != content_type: return HttpResponse(json.dumps({'message': 'invalid image mime-type'}), headers=headers, status=400)
Once all of the above checks passes, we can process the image.
Host your Django Application for free on PythonAnyWhere.
If you want complete control of your application and server, you should consider DigitalOcean. Create an account with this link and get $100 credits.