Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django prevent image upload containing possible XSS code

I am creating a site that users can upload images. I am using django-storages to forward these images to S3 bucket, but I recently read the security docs on Django's site: https://docs.djangoproject.com/en/3.0/topics/security/#user-uploaded-content

Django’s media upload handling poses some vulnerabilities when that media is served in ways that do not follow security best practices. Specifically, an HTML file can be uploaded as an image if that file contains a valid PNG header followed by malicious HTML. This file will pass verification of the library that Django uses for ImageField image processing (Pillow). When this file is subsequently displayed to a user, it may be displayed as HTML depending on the type and configuration of your web server.

It tells me about this vulnerability but it does not provide me an efective way of protecting against these vulnerabilities. Which is the top 3rd most vulnerable attack in websites.

Consider serving static files from a cloud service or CDN to avoid some of these issues.

I am using S3 to serve my media files, it does say to avoid some of the vulnarabilities described in the section, but is does not say which.

My question: Is uploading and serving images to and from AWS S3 vulnerable to these attacks, and if it does not, what is an effective way of sanitizing the content of the image ?

Edit for bounty: I host the images on S3, what are type of attack or vulnerabilities can happen ? And how to prevent such attacks ?

like image 571
Paaksing Avatar asked Aug 06 '20 23:08

Paaksing


People also ask

Does Django protect against XSS?

Using Django templates protects you against the majority of XSS attacks. However, it is important to understand what protections it provides and its limitations.

Which method is used to prevent XSS in Django?

Use of the | safe filter​ The | safe filter marks the content as "safe for rendering." This has the same effect as mark_safe() in Python code. This will permit direct rendering of HTML and create a possible XSS vulnerability.

Does Django encode input?

Django by default uses Unicode and UTF-8 encoding. When you give input, Django forcefully encodes it and then escapes dangerous characters. But this doesn't work for base64 encoded strings. In the above example, where we saw that some characters are replaced with the HTML code, the input is considered as UTF-8.


2 Answers

Why not just verify that the file is a valid image?:

from PIL import Image
image = Image.open(file)
image.verify()

As another poster has suggested, you can indeed attempt a transformation and check if an exception is thrown, but verify() will probably be quicker.

Or maybe you can try detecting the type?:

import imghdr
path = 'Image.jpg'
imghdr.what(path)

Or

from PIL import Image
image = Image.open('myimage.png')
image.format

Using any of the above methods, you can determine if the file is actually an image or not. If it is not an image, then consider the file as spurious, and do not output it on any of your web pages. By not outputting the file, there is no risk of XSS from this vector, because even if the file is HTML, by not outputting it on your page, it cannot compromise your page.

like image 108
Rahul Iyer Avatar answered Sep 22 '22 05:09

Rahul Iyer


The best possible solution comes from the Pillow library itself.

Even if someone can Manipulate the headers of an HTML File to make them look as PNG files, when you try doing some operation on them (say resizing), it will simply not work and throw an error, so you can capture it inside a try except block and warn/flag the user for malicious intent.

If you don't want to reduce the quality of any image given to you, then you can resize to the original image size, it will work without compromising the quality of image.

like image 30
Shubham Gupta - TCH Avatar answered Sep 21 '22 05:09

Shubham Gupta - TCH