Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding nonce to script tags rendered by Django Form Assets (Media class) #119

Open
Tracked by #185
hleroy opened this issue May 1, 2019 · 8 comments
Open
Tracked by #185
Labels
feature new features

Comments

@hleroy
Copy link

hleroy commented May 1, 2019

Django allows to associate different files – like stylesheets and scripts – with the forms and widgets that require those assets, using the Media class described here.

I have looked at the source code for Django's widgets.py, but I can't find an elegant way to add the nonce to the rendered <script> tag.

Could you suggest a workaround ? Or is this something that should be addressed in Django itself ?

Thanks for your help.

@dmptrluke
Copy link

If this is still an issue, I have created a workaround.
https://github.com/dmptrluke/django-csp-helpers/blob/master/csp_helpers/templatetags/media_csp.py

It is a template tag you can use ({% media_csp my_form %}) that will take the form object and output the media tags with CSP nonces.

@hleroy
Copy link
Author

hleroy commented Dec 17, 2019

Tested, it works :) It would be cool if it could be included in django-csp

@dmptrluke
Copy link

I've expanded django-csp-helpers with proper documentation and some mixins that allow you to freely use CSP nonces in both form media and widget templates!

https://github.com/dmptrluke/django-csp-helpers

@fallen
Copy link

fallen commented Jan 5, 2020

i'm indeed also using django-csp-helpers as a workaround. 👍

@g-k
Copy link
Contributor

g-k commented Jan 8, 2020

That's cool I wasn't aware of the Media class.

Assuming we can make people aware that they might be whitelisting assets from other domains similar to #127 (comment), then this seems like an extension of the template tags.

I'm open to landing Media class support in django-csp, but not planning on working on it.

I don't think it blocks #135 but django core might want the feature.

@g-k g-k added the feature new features label Jul 27, 2021
@DylanYoung
Copy link
Contributor

I can look into merging this in. I'm not super familiar with the logistics of the licensing stuff. Do I need to copy the license over from https://github.com/dmptrluke/django-csp-helpers/blob/master/LICENSE to somewhere in django-csp?

@some1ataplace
Copy link

some1ataplace commented Mar 24, 2023

To add a nonce to script tags rendered by Django Form Assets, you can:

  1. Define a custom Media class for your form or widget, and include the CSP_NONCE template context variable in the rendered HTML for the script tag:
   from django import forms
   from django_csp import settings as csp_settings
   
   class MyForm(forms.Form):
       class Media:
           js = ('my-script.js',)
           csp_nonce = csp_settings.CSP_NONCE
  1. In your template, include the {{ form.media }} variable to render the script tag with the CSP nonce:

{{ form.media }}

  1. If you want to customize the rendering of the script tag further, you can subclass the Media class and override the render_js method to include the CSP nonce:

from django.forms.widgets import MediaMixin
from django_csp import settings as csp_settings

class CustomMedia(MediaMixin):
    def render_js(self, attrs=None):
        csp_nonce = csp_settings.CSP_NONCE
        js = super().render_js(attrs)
        return js.replace('<script ', f'<script nonce="{csp_nonce}" ')

After the code is implemented, the CustomMedia class will be used to render script tags for Django form assets (such as JavaScript files specified using the Media class in MyForm). The render_js method sets a nonce attribute on each script tag, using the value from CSP_NONCE specified in the Django CSP settings.

Lastly, to add a nonce to other script tags in your Django views, you can use the csp_nonce variable in your HTML template like this:

<script nonce="{{ csp_nonce }}" src="https://example.com/my_script.js"></script>


To add a nonce to script tags rendered by Django Form Assets (Media class) using django-csp, you can create a custom widget that adds the nonce to the script tag. Here is an example:

from django.forms.widgets import MediaDefiningClass
from django_csp.utils import get_nonce

class NonceMediaDefiningClass(MediaDefiningClass):
    def __new__(cls, name, bases, attrs):
        new_class = super().__new__(cls, name, bases, attrs)
        if 'media' in attrs:
            for media_type in ('css', 'js'):
                if media_type in new_class.media:
                    for i, media_def in enumerate(new_class.media[media_type]):
                        if isinstance(media_def, dict) and 'src' in media_def:
                            media_def['nonce'] = get_nonce()
                            new_class.media[media_type][i] = media_def
        return new_class

This custom widget class extends Django's MediaDefiningClass and adds a nonce to any script tags in the widget's media definition. The get_nonce() function is provided by django-csp and generates a random nonce for each request.

To use this custom widget class, you can create a new widget that inherits from TextInput (or any other widget) and sets metaclass to NonceMediaDefiningClass:

from django.forms.widgets import TextInput
from .utils import NonceMediaDefiningClass

class MyWidget(TextInput):
    __metaclass__ = NonceMediaDefiningClass
    # Widget code here

This will ensure that any script tags rendered by the widget will include a nonce attribute with a random value.


If you are copying code over from another open-source project with a different license, you should always make sure that you comply with the terms of the original project's license.

In this case, it looks like django-csp-helpers is licensed under the MIT License. If you are copying code from django-csp-helpers to django-csp, you should ensure that you include a copy of the MIT License in the LICENSE file of django-csp or somewhere else in the project directory.

It's also a good practice to provide attribution to the original project and its contributors in your project's documentation, README file, or relevant sections of your code.

When copying code from django-csp-helpers to django-csp, you should include the MIT License with the copied code. You can do this by adding a copy of the LICENSE file from django-csp-helpers to django-csp, or by including the license text in a comment at the top of the copied code. This ensures that the license terms are clear and that users of the code understand their rights and obligations.

@dmptrluke
Copy link

Hi!

While MIT and BSD are largely the same license, I give permission for code from django-csp-helpers to be added to this project under the existing license without including any extra licensing or attribution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature new features
Projects
None yet
Development

No branches or pull requests

6 participants