Opened 3 weeks ago

Closed 11 days ago

#37419 closed Bug (fixed)

Include CSP nonces in headers for streaming responses

Reported by: Sarah Boyce Owned by: Sarah Boyce
Component: CSP Version: 6.1
Severity: Normal Keywords:
Cc: Rob Hudson, Jake Howard Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Background:
The security team received a report from Michael Codispoti (codispoti) on HackerOne. This report was agreed to be handled in public as a hardening as the CSP middleware doesn't absolve developer responsibility to escape untrusted input.

Issue:
For a StreamingHttpResponse, the response headers are constructed before the streaming iterator is consumed.
If the iterator first accesses csp_nonce while producing the response body, the HTML receives a generated nonce after the CSP header has already been finalized.

This causes the streamed HTML to contain a nonce that is absent from the corresponding Content-Security-Policy header.

Regression test:

  • tests/middleware/test_csp.py

    diff --git a/tests/middleware/test_csp.py b/tests/middleware/test_csp.py
    index f0a4af2a7c..c64754c221 100644
    a b from playwright_tests import PlaywrightTestCase  
    22from utils_tests.test_csp import basic_config, basic_policy
    33
    44from django.contrib.staticfiles.testing import StaticLiveServerTestCase
    5 from django.test import SimpleTestCase
     5from django.http import StreamingHttpResponse
     6from django.middleware.csp import ContentSecurityPolicyMiddleware
     7from django.template import Context, Template
     8from django.template.context_processors import csp
     9from django.test import RequestFactory, SimpleTestCase
    610from django.test.utils import modify_settings, override_settings
    711from django.utils.csp import CSP
    812
    … … class CSPMiddlewareWithDecoratedViewsTest(SimpleTestCase):  
    173177        )
    174178
    175179
     180class CSPStreamingTests(SimpleTestCase):
     181    @override_settings(
     182        SECURE_CSP={
     183            "default-src": [CSP.SELF],
     184            "script-src": [CSP.NONCE],
     185        },
     186        SECURE_CSP_REPORT_ONLY={},
     187    )
     188    def test_nonce_generated_during_streaming_is_in_header(self):
     189        request = RequestFactory().get("/")
     190        template = Template('<script nonce="{{ csp_nonce }}"></script>')
     191
     192        def view(request):
     193            def content():
     194                yield template.render(Context(csp(request)))
     195
     196            return StreamingHttpResponse(content())
     197
     198        response = ContentSecurityPolicyMiddleware(view)(request)
     199        body = b"".join(response.streaming_content).decode()
     200
     201        nonce = body.split('nonce="', 1)[1].split('"', 1)[0]
     202        self.assertTrue(nonce)
     203        self.assertIn(f"'nonce-{nonce}'", response["Content-Security-Policy"])
     204
     205
    176206@override_settings(
    177207    ROOT_URLCONF="middleware.urls",

Change History (11)

comment:1 by Sarah Boyce, 3 weeks ago

Has patch: set

comment:2 by Sarah Boyce, 3 weeks ago

Cc: Rob Hudson Jake Howard added

comment:3 by Jake Howard, 3 weeks ago

Patch needs improvement: set

comment:4 by Sarah Boyce, 3 weeks ago

Patch needs improvement: unset

comment:5 by Jacob Walls, 3 weeks ago

Triage Stage: Unreviewed → Accepted

I guess we're taking away the ability of streaming responses to *not* have a nonce applied, but I suppose Django should solve the 80% use case and leave that detail to be worked out with custom middleware if needed.

comment:6 by Jacob Walls, 3 weeks ago

Triage Stage: Accepted → Ready for checkin

comment:7 by Sarah Boyce, 3 weeks ago

Triage Stage: Ready for checkin → Accepted

comment:8 by Jacob Walls, 12 days ago

Component: Core (Other) → CSP

comment:9 by Jacob Walls, 12 days ago

Triage Stage: Accepted → Ready for checkin

comment:10 by Sarah Boyce <42296566+sarahboyce@…>, 11 days ago

In 4e7c04fd:

Refs #37419 -- Moved CSP nonce detection helper to django.utils.csp for middleware reuse.

comment:11 by Sarah Boyce <42296566+sarahboyce@…>, 11 days ago

Resolution: → fixed
Status: assigned → closed

In 3d3d943:

Fixed #37419 -- Included CSP nonces in headers for streaming responses.

Thank you Michael Codispoti (codispoti) for the report. Thank you to
Jake Howard, Jacob Walls and Rob Hudson for the reviews.

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