20% of Django websites are vulnerable to these 3 hacks
Tags: django, webdev, python, security
Published: 2021-01-06 12:56:12

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](https://django.doctor/advice/C4007)
- 6% were vulnerable to clickjacking [read more](https://django.doctor/advice/C4008)
- 5% were vulnerable to CSRF attack [read more](https://django.doctor/advice/C4009)

Django docs suggests [SecurityMiddleware](https://docs.djangoproject.com/en/3.1/ref/middleware/#module-django.middleware.security) 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](https://django.doctor/challenge/settings-security)._

### 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):

{% youtube -_8x-ygK-ck  %}

See [my previous post for more details and working proof of concept](https://dev.to/djangodoctor/django-hacking-clickjacking-4hg2)

### 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:

1. The user accesses the malicious website
2. The website executes JavaScript to perform a request to your website
2. The browser exposes the user's cookies to your website
4. Your website thinks the request came from them

Here's a concrete example of a CSRF attack:

{% youtube C22BEW7AkrM %}

See [my previous post for more details and working proof of concept](https://dev.to/djangodoctor/hacking-django-websites-cross-site-request-protection-2i1i)


### 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:

```py

# 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:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/o9anfdz0cp3znsbihcvc.png)

Such attacks can be avoided if the `SecurityMiddleware` is present and the `nosniff` feature is active:

```py
# 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:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/yv542dgedp18d6w3bnjw.png)

### 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](https://developer.mozilla.org/en-US/docs/Web/Security/Referer_header:_privacy_and_security_concerns) 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](https://django.doctor), or can [review your GitHub PRs](https://github.com/marketplace/django-doctor/):

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/d10epulva9d373jgldhu.png)

Or try out [Django security challenge](https://django.doctor/challenge/settings-security).