Adding CSP support to Django

Hi everyone,

I’m writing to express interest in adding CSP support to Django. The motivation behind this is to provide built-in support for Content Security Policy (CSP), a crucial security feature that helps mitigate a range of attacks such as Cross-Site Scripting (XSS) and data injection attacks.

There’s a Django bug for this:
https://code.djangoproject.com/ticket/15727

I have reviewed previous discussions and seen several PRs that never quite made it over the finish line. The general consensus seems to be that most people agree this would be beneficial for Django, although there’s some disagreement on some of the finer details. I’m hoping to spark discussion on those and find consensus.

As the current maintainer of django-csp, I have a good understanding of its strengths and areas for improvement. While I don’t think django-csp should be copied verbatim, I’m willing to adapt the good parts to fit Django standards. I’d love it if this could make it into Django 5.2, and alongside this, I’d like django-csp 4.0 to match the configuration style. This way, it would serve as a good third-party package for those not yet on Django 5.2, while also minimizing the work required to upgrade.

I have initiated a branch if you’d like to see the current direction, but wanted to share my intention here before creating a draft PR to get some general feedback and gauge interest. The branch includes several “TODO” comments on areas I consider open for discussion, which would be easier to comment on once a draft PR is published:

Looking forward to your thoughts and collaboration.

Best regards,
Rob

9 Likes

I have reviewed previous discussions and seen several PRs that never quite made it over the finish line. The general consensus seems to be that most people agree this would be beneficial for Django, although there’s some disagreement on some of the finer details.

Would you mind referencing these discussions? Just so that curious people can read up before responding :slight_smile:

I’ll gladly admit that CSP often slips my mind in projects, simply because Django provides lots of security, so it’s easy to get lazy and assume that it’s all taken care of. I think I’ve mostly added it in Nginx, but maybe also with django-csp in the earlier days.

It would seem like - no matter the outcome of this discussion - the Django Docs topic on Security could have a section on CSP.

Edit: Now proposed some documentation: Docs: Add CSP to Security topic by benjaoming · Pull Request #18212 · django/django · GitHub

Sure! There may be others but this is what I could find again from browser history…

django-developers list:
A comment from James Bennett, “Content Security Policy support in core. The current django-csp third-party app isn’t necessarily bad, but I’d love to see more good security tools in Django by default.” referenced here

PR from 2014:

PR from 2015:

PR from 2023:

I think I’ll create my own draft PR for my branch now to make it easier to discuss.

2 Likes

Hi @robhudson

really cool that you picked that up. I’m wondering why Adam Johnson didn’t yet jump on this train. If I remember correctly, he argued in favour of this at DjangoCon Europe 2018.

If we add something like this, I’m wondering if we should make Django compliant as well. AFAIK there is an issue with the admin that you have to have unsafe-inline to be set, otherwise some features don’t work. I couldn’t reproduce it on the fly. Does anybody know more about that?

Best from Cologne
Ronny

Hi @GitRon ,

I believe the unsafe-inlines in the Django admin were fixed. Here’s the closed bug about that: #25165 (Move JavaScript calls out of HTML to fix JavaScript "no-script-eval" warnings) – Django

Cheers!
-Rob

You are right! I know I had issues but I just set up a vanilla Django project and it works. Sorry for the false input!

Hello @robhudson, thank you for starting this thread! I’m not (yet?) all familiar with CSP but I know that “Bring CORS and CSP into core” was proposed (but not progressed) both for Google Summer Of Code 2023 and 2024 editions.

With that in mind, and because we would need some guidance of the Steering Council regarding a few potentially contentious topics such as adding a couple of new settings, I think we should summon @steering_council (which coincidentally includes Adam and James who were listed as potential mentors for this GSoC project in 2023) for them to provide some guidance on the next steps.

As far as my understanding of DEP goes, this wouldn’t require one since we have the accepted ticket, but it would be benficial if you could, Rob, to clearly list the items that may need resolution and the options for each one in order to ease the decision making process.

Also for the rest of the readers, the draft proposal mentioned by Rob is Fixed #15727 -- Added Content Security Policy (CSP) support. by robhudson · Pull Request #18215 · django/django · GitHub

1 Like

I don’t think adding CSP support is large enough to require a DEP. If it were going to be a larger overhaul of the security middleware, maybe, but I think CSP can be added without one, and probably CORS can be added too without one.

One thing that’d be really nice to get out of this, but is not a hard requirement, would be to consolidate the configuration into a top-level SECURITY or SECURITY_MIDDLEWARE setting as noted in that linked thread – right now the config for all the things SecurityMiddleware does is a bit complicated and fragmented.

Like, maybe the way this gets done is

  1. Add support to SecurityMiddleware for reading from a SECURITY dict in settings, falling back to the old individual settings when SECURITY is not present, deprecate the old individual settings.
  2. To encourage people to migrate to it, CSP is introduced as a feature only supported via the SECURITY dict
  3. Same approach for CORS
3 Likes

It would be nice with a discussion around the default behavior and what conventions to establish through documentation examples / django startproject (or pardons if I missed that, the historic track is really long). Another example of default behavior is for DEBUG=True, should we then switch off CSP (same as ALLOWED_HOSTS)? Or does it somehow magically add localhost-like addresses? Maybe django-csp has some experience to reuse!

Even if we do add CSP, do we just allow all contents and hosts * ? Do we switch on reporting etc? How much should the new tutorial user be bothered?

It’s also important to ask how this security layer should be documented - should we want to put emphasis on some preference for having it in the HTTP server? To me, it’s not obvious that Django should be adding these headers if the developer has access to her HTTP server’s configuration.

Maybe a way forward could be to 1) introduce opt-in CSP support in core and 2) start the discussion around default behavior for a subsequent release?

I don’t think adding CSP support is large enough to require a DEP.

Side-track about the role of DEPs

It should not be the size of the implementation that warrants a DEP. I’m sure that a well-written DEP for a change that can be perceived as small is also welcome, if it helps to guide the subsequent implementation in the right direction. In this case, we’ve seen multiple efforts go down the drain, and I would speculate that a DEP could have helped! Rather than code footprint, it’s more a question of decisions/vetting/approval/complexity/communication. If someone wanted to go the DEP-way with this, I think that could work really well, too :+1: Your proposal for a single SECURITY setting also sounds like DEP material, if it was to happen :slight_smile: (I can think of a number of reasons why this simple proposal is also complex at the same time).

I reached out to @adamchainz while here at DjangoCon and it was suggested that CSP be in its own middleware, like CSRF or click-jacking, as adding CSP to SecurityMiddleware would bloat things. I’m adjusting my branch at the djangocon sprints to follow this recommendation.

I did consider the changes to the various settings that affect the SecurityMiddleware, and I do agree, organizing them under a SECURITY dict would be nice. I did start on a patch but then felt like I was hading off on a tangent so I paused work there. If that’s something that’s desired I’d be happy to help out after CSP work, but would like to stay focused on that for the moment.

3 Likes

Hi @benjaoming!

You raise a lot of important points and considerations.

In my view, Django should include opt-in support for CSP. However, I don’t believe Django should provide a default CSP configuration. Each project has unique requirements and will need to tailor a CSP to fit its specific needs. It’s also up to the project’s developers to decide whether to apply the CSP in report-only mode or enforced mode, depending on the nature and security needs of their project.

The django-csp project does disable CSP when DEBUG=True and for 404s or 500 errors to ensure Django’s debug view runs without issues. Aside from that, enabling CSP is useful for local development and believe django-csp’s approach is also a good fit for Django.

1 Like

@robhudson I’d love to chip in on the documentation-side of things, once people agree on the various concerns and considerations that should be put into effect. I started a PR already which I think can precede this effort. Like a little step-stone. What do you think? Docs: Add CSP to Security topic by benjaoming · Pull Request #18212 · django/django · GitHub

Documentation help would be amazing. It was requested to make CSP docs their own similar to CSRF. My plan was to wait until all the code was in place before working on documentation but I do have piece here and there, not checked-in, that I was getting a start on.