A website served via HTTP is vulnerable to Man In The Middle (MITM) attacks: a hacker can get between your browser and the server responding to the browser’s requests. The response or request can be amended for malicious intent. A Man could get In The Middle after an unsuspecting user connects to a nefarious network e.g. when joining a cafe’s wifi or after a cheeky connection to a neighbor’s unprotected network: these may be honey traps.
from bs4 import BeautifulSoup
from django.http import HttpResponse
# after 2 seconds change some content
document.querySelectorAll("h1").innerText = "HACKED!"
def dispatch(self, request, *args, **kwargs):
# user may be logging in, so save the form data so to maybe steal their username and password
save_form_data(request.GET or request.POST)
# cookies may contain session cookie, so save it to later maybe do session hijacking
# user may be doing something embarrassing, so save the url to maybe blackmail them
# user may be uploading some embarrassing pictures of documents. more blackmail
response = super().dispatch(request=request, path=request.get_full_path(), *args, **kwargs)
if 'text/html' in response.get('content-type'):
soup = BeautifulSoup(response.content, 'html.parser')
response = HttpResponse(str(soup))
And this is the outcome:
This can be avoided by serving exclusively on HTTPS as the content will no longer be in plain text for the MITM to read and mutate. Django supports this via
SECURE_SSL_REDIRECT - so Django will redirect any HTTP request to HTTPS. However, this is an incomplete solution:
- a MITM could intercepts the “redirect to HTTPS” response and change it.
- a MITM could upgrade your HTTP request to HTTPS: the user has a HTTP request that terminates at the MITM and the MITM upgrades the request to HTTPS: data would be plainly readable by the bad actor.
There is a solution to that in HTTP Strict Transport Security protection: the browser blocks HTTP requests to your website and instead use HTTPS.
Django facilitates that via the
SECURE_HSTS_SECONDS setting. When first setting the value it's worth using a small value like 3600 (1 hour) to check it works as expected, as once the browser sees the HSTS header it will respect it until the specified time is met, meaning if your website has misconfigured HTTPS certificates then you cannot rollback to HTTP while you fix it.
It’s also advisable to set
SECURE_HSTS_INCLUDE_SUBDOMAINS so the browser uses HTST for all subdomains and not just the current one. It would be a shame to protect http://example.com but not http://www.example.com.
So concretely the following change will help protect your Django website against Man In The Middle attacks:
SECURE_HSTS_ settings required
django.middleware.security.SecurityMiddleware to be present in
MIDDLEWARE otherwise they will do nothing.
Does your website have security vulnerabilities?
Ready for a Django security challenge? Play our Django security challenge.