Opened 3 weeks ago

Last modified 14 hours ago

#36784 assigned New feature

Add CSP support to Django's script object and media objects

Reported by: Johannes Maron Owned by: Nilesh Pahari
Component: Forms Version: 6.0
Severity: Normal Keywords:
Cc: Johannes Maron, Rob Hudson, Tobias Kunze, David Smith Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Django 5.2 added official support for a script object in media classes #35886

However, the introduction of CSP's nonce-function in Django 6.0 seems to have overlooked both old form media rendering and the script-object.
Furthermore, the template processor-based approach currently doesn't provide practical solution of object based media assets.

I'd suggest updating the media class and tag-rendering to include a nonce values by default, or the least an opt-in that doesn't require the request context in during form definition.

If there already is an easy way to add nonce-values to form media, I'd suggest that we add a few lines of documentation for the next developer looking for it.

Change History (12)

comment:1 by Natalia Bidart, 3 weeks ago

Type: UncategorizedNew feature

Hello Johannes! Thank you for this report. Could you please share a small project sample that would highlight how CSP is lacking support for the cases you listed? That would help me greatly to properly triage this ticket.

comment:2 by Natalia Bidart, 3 weeks ago

Cc: Rob Hudson added

Adding Rob as cc for awareness.

comment:3 by Natalia Bidart, 3 weeks ago

Cc: Tobias Kunze David Smith added
Triage Stage: UnreviewedAccepted

I've been thinking about this and I see a couple of options:

  • A decent workaround would be to define a template filter that would take the nonce and include it in the tag. We could perhaps write a how-to to backport and include in 6.0.
  • For main, I agree that we should ideally have something more "first class citizen" in the objects. I'm adding a few folks as cc to see what they think.

Given the above, I'll accept pending a design discussion for the "new feature" part for 6.1. In any case, Johannes it would be super helpful if you could attach a minimal sample project showing the use cases.

comment:4 by Pravin, 3 weeks ago

Any thoughts on below failing testcase for above behaviour ?

from django.test import SimpleTestCase, override_settings
from django.forms import Form
from django.template import Context, Template
from django.utils.csp import CSP

class FormWithJsMedia(Form):
    class Media:
        js = ["path/to/js_file.js"]

@override_settings(
    STATIC_URL="/static/",
    MIDDLEWARE=[
        "django.middleware.security.SecurityMiddleware",
        "django.middleware.csp.ContentSecurityPolicyMiddleware",
    ],
    TEMPLATES=[{
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.request",
                "django.template.context_processors.csp",
            ],
        },
    }],
    SECURE_CSP={
        "default-src": [CSP.SELF],
        "script-src": [CSP.SELF, CSP.NONCE],
    }
)
class CSPMediaTest(SimpleTestCase):
    def test_form_media_js_missing_nonce(self):
        form = FormWithJsMedia()
        tpl = Template("{% load static %}{{ form.media }}")
        rendered = tpl.render(Context({"form": form}))
        self.assertIn('<script src="/static/path/to/js_file.js">', rendered)
        self.assertIn('nonce="', rendered)

comment:5 by Rish, 3 weeks ago

Owner: set to Rish
Status: newassigned

comment:6 by Rob Hudson, 3 weeks ago

The challenge seems to be that form.media does not have access to the context and is stateless. I assume this is by design. Changing this seems like a big architectural shift so I looked for other options.

One idea that I liked has two parts to it:

  1. Extend the Script class to add a with_nonce: bool = False parameter.

Example:

class MyWidget(forms.TextInput):
    class Media:
        js = [
            "already-in-policy.js",  # No nonce needed
            Script("inline-script.js", with_nonce=True),  # Opt-in to nonce
        ]

This would render the script tag with a data attribute - something harmless if the next step is forgotten (vs something like a nonce attribute with a sentinel):

    <script src="..." data-csp-nonce></script>

I like the opt-in nature of this vs outputting all tags with a data attribute since, if the media is self served you likely don't need the nonce.

  1. Use a template filter to replace data attribute with the actual nonce
    {{ form.media|with_nonce }}

The filter:

  • finds and replaces the data-csp-nonce attribute with the actual nonce from template context.
  • if no nonce in the context, removes the data attribute.

comment:7 by Johannes Maron, 3 weeks ago

@Rish, if you don't mind, I was hoping to solve this myself. Did you make any progress yet, you'd care to share?

@Rob, I was thinking to use template nodes, instead of HTML-safe strings. So the asset objects would be rendered with the full temple context, including a nouce.
If the template includes it, we render it. Otherwise we don't. Of course, this could be added explicitly with a keyword, as you suggested.

in reply to:  7 comment:8 by Rish, 11 days ago

Replying to Johannes Maron:

@Rish, if you don't mind, I was hoping to solve this myself. Did you make any progress yet, you'd care to share?

@Rob, I was thinking to use template nodes, instead of HTML-safe strings. So the asset objects would be rendered with the full temple context, including a nouce.
If the template includes it, we render it. Otherwise we don't. Of course, this could be added explicitly with a keyword, as you suggested.

Sorry for blocking you, I am new to this. You can have the ticket.

comment:9 by Rish, 11 days ago

Owner: Rish removed
Status: assignednew

comment:10 by Nilesh Pahari, 41 hours ago

Owner: set to Nilesh Pahari
Status: newassigned

in reply to:  10 ; comment:11 by Laharyy, 14 hours ago

Replying to Nilesh Pahari: Hi, I noticed this ticket is currently assigned.

I’m interested in working on this issue and have started reviewing the Media
and script rendering internals. Please let me know if anyone is actively
working on it; otherwise I’d be happy to take this forward.

in reply to:  11 comment:12 by Nilesh Pahari, 14 hours ago

Replying to Laharyy:

Replying to Nilesh Pahari: Hi, I noticed this ticket is currently assigned.

I’m interested in working on this issue and have started reviewing the Media
and script rendering internals. Please let me know if anyone is actively
working on it; otherwise I’d be happy to take this forward.

Hi, thanks for your interest. I’m currently working on this, but I’ll definitely let you know if I need any help.

Note: See TracTickets for help on using tickets.
Back to Top