Skip to content

Django and Content Security Policy

Django has a default enabled Middleware dealing with XSS (Cross Site Scripting attacks) and Clickjacking protection: XFrameOptionsMiddleware. This middleware has the default setting of SAMEORIGIN which allows iFrames only from the same domain as the name suggests.

But what if you need to embed your Django generated HTML page in another domains webpage with an iFrame? Bummer, it doesn’t work out of the box …

The XFrameOptionsMiddleware is configurable via X_FRAME_OPTIONS in settings.py. The only two allowed values are DENY and SAMEORIGIN. So not much of a help for our problem.

One approach is to subclass the middleware and overwrite the get_x_frame_0ptions_value method:

class CustomXFrameOptionsMiddleware(XFrameOptionsMiddleware):
    """Customize Clickjacking Middleware to disable uppercase options.

    allows custom configuration using X_FRAME_OPTIONS in the settings
    file. e.g. X_FRAME_OPTIONS = 'allow-from \'https://example.com/\''
    """
    def get_xframe_options_value(self, request, response):
        """supress UPPERCASING X_FRAME_OPTIONS from settings file."""
        return getattr(settings, 'X_FRAME_OPTIONS', 'SAMEORIGIN')

The only thing we do is to remove the .upper() from the gettattr to allow us to set the X_FRAME_OPTIONS freely in setting.py, e.g. X_FRAME_OPTIONS = 'allow-from http://example.com/' 

Great, we now have a working configuration! Right? Nope!

The problem with x-frame-options is, that it’s not supported in Safari and Chrome at all and some other browser don’t support the allow-from directive.

Let me introduce Content Security Policy. This settings are available in all current Browsers and there is a Django Extension for it: https://github.com/mozilla/django-csp and even with Documentation 😉

It’s easy to use: install using pip with pip install django-csp  and adding the middleware to your settings.py file in the MIDDLEWARE_CLASSES section:

'csp.middleware.CSPMiddleware',<br>

Now comes the tricky part: you’re protected but maybe too well protected as the default content security policy is set to ‘self’ and only allows images, frames, fonts, styles from the SAMEDOMAIN. If you happen to include e.g. fonts from google or jQuery from a CDN location in the net these resources get blocked by CSP! But it’s easy to debug in the Developer Tools of Chrome, Safari, Firefox and even Edge.

Read the documentation on https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy for possible parameters and https://django-csp.readthedocs.io/en/latest/configuration.html for how to add it to Django in your settings.py file.

In my example I had linked to some fonts on Google:

CSP_STYLE_SRC = ("'self'", "'unsafe-inline'", 'fonts.googleapis.com')
CSP_FONT_SRC = ("'self'", 'fonts.gstatic.com')
CSP_FRAME_ANCESTORS = ("'self'", 'example.com')

Breakdown of the values used:

‘self’ is the same as SAMEORIGIN in the x-frame-options setting.
‘unsafe-inline’ is needed to allow inline styles (<style> …. </style>).

Notable is that we have to both allow style sources and font sources from Google as the linked stylesheet from Google itself links to fonts on another domain than our own.

frame-ancestors or CSP_FRAME_ANCESTORS is the setting which allows defined domains (with or without protocol, e.g. https://example.com) to embed these webpages in an iFrame.

We’re done!

If you don’t have to support Internet Explorer 10 or 9 or other outdated Browsers. These browsers don’t support CSP only x-frame-options and most of them only the DENY and SAMEORIGIN settings. So update your Browsers!!!! 😉

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.