Opened 67 minutes ago
#37395 new New feature
Add a system check for a CSP directive whose only source is CSP.NONCE (silently fails open)
| Reported by: | venkatchalla06 | Owned by: | |
|---|---|---|---|
| Component: | Core (System checks) | Version: | dev |
| Severity: | Normal | Keywords: | |
| Cc: | venkatchalla06 | Triage Stage: | Unreviewed |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
Django 6.0 added built-in CSP. In django.utils.csp.build_policy(), when a directive's only source is CSP.NONCE and the per-request nonce was never accessed (the LazyNonce stays falsy because the response rendered no {{ csp_nonce }}), the sentinel is stripped, the directive becomes empty, and the directive is dropped from the header entirely. For the same configuration, the emitted policy then depends on whether the nonce happened to be used:
{"default-src": ["*"], "script-src": [CSP.NONCE]}— nonce used:default-src *; script-src 'nonce-...'; nonce unused:default-src *(script-src removed, scripts fall back to*).{"script-src": [CSP.NONCE]}— nonce unused: an emptyContent-Security-Policyheader (no policy at all).{"script-src": [CSP.SELF, CSP.NONCE]}(recommended) — nonce unused:script-src 'self'(fails closed, unaffected).
So a nonce-only directive silently degrades to its default-src fallback (possibly permissive) or to no policy, on exactly the responses most likely to carry injected markup (error pages, views with no inline nonce'd element). The recommended form — pairing CSP.NONCE with an explicit fallback — already fails closed.
Proposed: a new system check, security.W028, that warns when any SECURE_CSP or SECURE_CSP_REPORT_ONLY directive's only source is CSP.NONCE, hinting to add CSP.SELF, CSP.STRICT_DYNAMIC, or CSP.NONE. It mirrors the existing security.W027 and makes no runtime behavior change. A patch with tests and docs is ready.
An alternative would be to make build_policy() fail closed (keep the directive present as block-all when the nonce is unused), but that changes documented output and would need a release note; a check seems the lower-risk fix.