Code Review Doctor
Posted on January 6, 2021
Django Doctor audits code and auto fixes Django anti-patterns. We checked 666 Django projects for insecure settings.py configurations and were shocked that 20% of the Django websites had these vulnerabilities:
- 8% had not activated SecurityMiddleware read more
- 6% were vulnerable to clickjacking read more
- 5% were vulnerable to CSRF attack read more
Django docs suggests SecurityMiddleware is placed near the top of your MIDDLEWARE
settings for good reason: it performs a suite of security checks and enhancements that would otherwise leave your website to the mercy of some simple hacks.
How would you solve these security flaws? Try our Django security challenge.
Clickjacking
Clickjacking is an attack where one of your logged-in user visits a malicious website, and that website tricks the user into interacting with your website via an invisible iframe (which in the demo I made mostly transparent otherwise you would see nothing interesting):
See my previous post for more details and working proof of concept
Cross Site Request Forgery
A malicious website that is accessed by one of your logged in users can fool your website into thinking a request come from that user. That may look like this:
- The user accesses the malicious website
- The website executes JavaScript to perform a request to your website
- The browser exposes the user's cookies to your website
- Your website thinks the request came from them
Here's a concrete example of a CSRF attack:
See my previous post for more details and working proof of concept
Tricking Django into executing uploaded javascript
SecurityMiddleware
prevenst hackers from tricking your website into executing a malicious javascript file that they uploaded by setting the X-Content-Type-Options
header to nosniff
.
This header indicate to the browser that the when the MIME types advertised in the Content-Type headers is not present, the browser should not guess it by "sniffing" what the content "smells" like. The browser uses the MIME type to determine to render HTML, execute JS, etc. Sniffing may be a very helpful feature for when servers are misconfigured and do not expose the Content-Type
header. But it can be abused, as the following simple proof of concept shows:
# settings.py
MIDDLEWARE = [
# "django.middleware.security.SecurityMiddleware",
"django.middleware.common.CommonMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
...
]
SECURE_CONTENT_TYPE_NOSNIFF = False # pre-Django 3.0 it was False by default. Post 3.0 it's True
# views.py
class HomePage(View):
def get(self, *args, **kwargs):
# simulate a misconfgured server that returns a response that has no content type
response = HttpResponse("<script>alert('hello world')</script>")
del response['Content-Type']
return response
The outcome when navigating to HomePage
shows the "hack" worked:
Such attacks can be avoided if the SecurityMiddleware
is present and the nosniff
feature is active:
# settings.py
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.middleware.common.CommonMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
...
]
SECURE_CONTENT_TYPE_NOSNIFF = True
Then the browser does not fall for the trick:
User privacy
Would you be happy if every hyperlink you clicked on told the target what website you came from? Aside from the privacy aspect, this leaks information to potential bad actors (e.g, if the url contained private information).
SecurityMiddleware
sets the referer policy header based on SECURE_REFERRER_POLICY
, which impacts user privacy and it leaks information for bad actors to abuse.
Is your Django code insecure?
Over time it's easy for security vulnerabilities to slip into your codebase. I can check that for you at django.doctor, or can review your GitHub PRs:
Or try out Django security challenge.
Posted on January 6, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.