DjangoCon Europe 2019: Django and Web Security Headers
Writeup of the DjangoCon Europe 2019 talk »Django and Web Security Headers« by Adam Johnson
Adam Johnson: I started programming when I was eight years old in good ol’ Quickbasic and have been hooked since. I’ve worked with Django since 2012, and have been a Django core contributor since 2016.
The web is a platform with a lot of backwards compatibility concerns and all browsers are trying to keep all the websites working for all time, be they modern HTTP2, or ancient HTTP. Since old stuff hangs around, the one thing that is okay to add and upgrade, are http headers.
There is a page to check your headers, and also the Mozilla observatory – check them out in any case, since you'll learn something about your page.
X-XSS-Protection
XSS stands for Cross-Site-Scripting (including code from different domains, mostly referring to malicious usage). Most browsers have XSS auditors on by default. They are not entirely backwards compatible, but they'll stop that one request.
If you set the mode to block
via your header, though, the XSS auditor will block the entire page.
In Django: set SECURE_BROWSER_XSS_FILTER = True
and include the SecurityMiddleware
in your middlewares.
Strict-Transport-Security
You should serve your site over HTTPS. Most websites are HTTPS, but problems can occur if your browser asks for HTTP first, since the request/redirect before the upgrade will be insecure. This header tells your browser to never use HTTP with that page ever again (within the limits of the timeout). There is also a huge list of domains with this enabled, to prevent any HTTP requests going out.
In Django: set SECURE_HSTS_SECONDS
to the timeout if the SecurityMiddleware
is active. Please ramp up the
seconds gradually – only enable includeSubdomains
and preload
when you're really sure. If you break your HTTPS
after having set this flag, any browser that has seen is will not be able to see your website again.
X-Content-Type-Options
Browsers guess content type with "MIME Sniffing". This backfires when user uploaded images are interpreted as HTML. You
can opt out by sending the header value nosniff
.
In Django: SECURE_CONTENT_TYPE_NOSNIFF = True
.
X-Frame-Options
Clickjacking embeds another webpage as an iframe, and the header tells browser to never embed the page (or only from okay pages).
In Django: X_FRAME_OPTIONS = 'DENY'
(or use whitelisting), and include the XFrameOptionsMiddleware
.
Referrer-Policy
Browsers pass the Referer
(with typo) with every request, telling the page where they came from. But this leaks
information – both personal information, and metadata. This header controls who gets this information.
In Django: Install django-referrer-policy
, add its ReferrerPolicyMiddleware
, and set REFERRER_POLICY =
'same-origin'
or other values.
Content-Security-Policy
This header stops your page from including content from any site. It's the strongest way to prevent XSS, clickjacking, and others. It's a huge complex topic. The header contains ; separated directives, like these:
Read about strict mode here, or consider starting with reporting mode to help with the roll-out in existing pages.
default-src 'self'; # by default only from this domain img-src *; # images from anywhere script-src userscripts.example.com # scripts only from here
In Django: Mozilla provides django-csp
, add CSPMiddleware
, and then set a couple of values, e.g.
CSP_DEFAULT_SRC = 'self'
.
Feature-Policy
New and not yet supported in all browsers – this header lets you disable browser feature, like autoplay, geolocation, camera, etc.
In Django: Install django-feature-policy
, add FeaturePolicyMiddleware
and set FEATURE_POLICY
.