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

Possible to override CSP_INCLUDE_NONCE_IN per-view with a decorator? #139

Closed
Tracked by #185
fallen opened this issue Dec 21, 2019 · 7 comments
Closed
Tracked by #185

Possible to override CSP_INCLUDE_NONCE_IN per-view with a decorator? #139

fallen opened this issue Dec 21, 2019 · 7 comments

Comments

@fallen
Copy link

fallen commented Dec 21, 2019

It would be nice to be able to use @csp_replace and @csp_update on CSP_INCLUDE_NONCE_IN.
Also, even @csp which is supposed to override everything, does not override the nonce generation.

Why?
Because some browsers like Chromium do not respect the style-src: 'unsafe-inline' if a nonce is given.
So if you generate a nonce with CSP_INCLUDE_NONCE_IN, for your entire project, but you want for some precise view to use 'unsafe-inline', you need to also have a mean to disable the nonce. Or else the browser will just ignore the 'unsafe-inline'.

@g-k
Copy link
Contributor

g-k commented Jan 6, 2020

Hey @fallen,

some browsers like Chromium do not respect the style-src: 'unsafe-inline' if a nonce is given

This is interesting.

Possible to override CSP_INCLUDE_NONCE_IN per-view with a decorator?

No this isn't possible, since CSP_INCLUDE_NONCE_IN is pulled from settings and not the header config that the other decorators operate on.

If it's OK to remove the nonce from all the directives, you could use a decorator like:

def csp_remove_nonces(f):
    @wraps(f)
    def _wrapped(*a, **kw):
        r = f(*a, **kw)
        r._csp_nonce = None
        return r
    return _wrapped

then since build_policy in utils strips the nonce sources when nonce is None. The full patch might be:

commit 4e29019150c71a2d9938b610afcae5ec88a9046e
Author: Greg Guthe <[email protected]>
Date:   Mon Jan 6 01:40:05 2020 -0500

    add csp_remove_nonces decorator (needs docs)

diff --git a/csp/decorators.py b/csp/decorators.py
index bce3352..69e1fab 100644
--- a/csp/decorators.py
+++ b/csp/decorators.py
@@ -10,6 +10,15 @@ def csp_exempt(f):
     return _wrapped
 
 
+def csp_remove_nonces(f):
+    @wraps(f)
+    def _wrapped(*a, **kw):
+        r = f(*a, **kw)
+        r._csp_nonce = None
+        return r
+    return _wrapped
+
+
 def csp_update(**kwargs):
     update = dict((k.lower().replace('_', '-'), v) for k, v in kwargs.items())
 
diff --git a/csp/tests/test_decorators.py b/csp/tests/test_decorators.py
index 6f2716e..0d377d8 100644
--- a/csp/tests/test_decorators.py
+++ b/csp/tests/test_decorators.py
@@ -2,7 +2,7 @@ from django.http import HttpResponse
 from django.test import RequestFactory
 from django.test.utils import override_settings
 
-from csp.decorators import csp, csp_replace, csp_update, csp_exempt
+from csp.decorators import csp, csp_replace, csp_update, csp_exempt, csp_remove_nonces
 from csp.middleware import CSPMiddleware
 
 
@@ -98,6 +98,29 @@ def test_csp():
     assert policy_list == ["default-src 'self'"]
 
 
+@override_settings(CSP_INCLUDE_NONCE_IN=["default-src"])
+def test_csp_remove_nonces():
+    # get a new REQUEST so we can modify ._csp_nonce without affecting other
+    # tests
+    request = RequestFactory().get('/')
+    request._csp_nonce = 'test-nonce'
+
+    def view_without_decorator(request):
+        return HttpResponse()
+    response = view_without_decorator(request)
+    mw.process_response(request, response)
+    policy_list = sorted(response["Content-Security-Policy"].split("; "))
+    assert policy_list == ["default-src 'self' 'nonce-test-nonce'"]
+
+    @csp_remove_nonces
+    def view_with_decorator(request):
+        return HttpResponse()
+    response = view_with_decorator(request)
+    mw.process_response(request, response)
+    policy_list = sorted(response["Content-Security-Policy"].split("; "))
+    assert policy_list == ["default-src 'self' 'nonce-test-nonce'"]
+
+
 def test_csp_string_values():
     # Test backwards compatibility where values were strings
     @csp(IMG_SRC='foo.com', FONT_SRC='bar.com')

@DylanYoung
Copy link
Contributor

FYI: this will be fixed in #36

@fallen
Copy link
Author

fallen commented Mar 10, 2020

FYI: this will be fixed in #36

Awesome, that's very good news!
Will this be documented?
How will it work then? Thanks :)

@DylanYoung
Copy link
Contributor

DylanYoung commented Mar 10, 2020

Haven't done up the docs yet, but will hopefully get them done along with some more extensive unit tests this week. In the single policy case, it will work just as you requested in the OP. In the multi-policy case, it can be overridden per policy, by passing 'include_nonce_in' in the policy dictionary ('report_only' works similarly).

:)

If all goes smoothly, I'm hoping we can get this merged and a release cut within a couple weeks.

@some1ataplace

This comment was marked as spam.

@robhudson
Copy link
Member

This will be possible in the upcoming release, currently under review in #219. The include-nonce-in directive is considered a "pseudo-directive" and can be set via the decorators.

@fallen
Copy link
Author

fallen commented May 3, 2024

This will be possible in the upcoming release, currently under review in #219. The include-nonce-in directive is considered a "pseudo-directive" and can be set via the decorators.

Very good news! Thanks a lot :)

robhudson added a commit to robhudson/django-csp that referenced this issue Jun 6, 2024
This is a backwards incompatible change.

Also fixes mozilla#139, mozilla#191
robhudson added a commit to robhudson/django-csp that referenced this issue Jun 6, 2024
This is a backwards incompatible change.

Also fixes mozilla#139, mozilla#191
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants