Add a Content Security Policy as defense in depth
Nobody has claimed this yet.
- Dominant language
- HTML
- Stars
- 31
- Forks
- 30
- Avg merge
- 1d 4h
- Merged PRs (30d)
- 4
Description
Add a Content Security Policy as defense in depth
Severity: Low
OWASP category: A02:2025 - Security Misconfiguration
Summary
The production site does not send a Content-Security-Policy header. A tested, enforcing policy would restrict the scripts and other resources that browsers may load if unsafe HTML is published.
The project renders stored HTML with Django's safe filter. CSP would provide a fallback if unsafe content passes the publishing workflow, but it would not replace server-side HTML sanitization.
Evidence
The response from https://python.ph/ did not contain a Content-Security-Policy or Content-Security-Policy-Report-Only header.
Stored HTML is rendered with safe in these public templates:
app/landing/templates/landing/header/index.htmlapp/landing/templates/landing/our_aim.htmlapp/landing/templates/landing/why_python.htmlapp/landing/templates/landing/what_we_do/index.htmlapp/landing/templates/landing/code_of_conduct.htmlapp/landing/templates/landing/mailing_list.htmlapp/events/templates/events/event_detail.htmlapp/jobs/templates/jobs/detail.html
Sections and events can be changed through Django admin or data imports. An authenticated user can submit a job description, but it is not public until the job is approved. These fields use tinymce.models.HTMLField, and the project does not sanitize their HTML on the server.
The lock file resolves Django 6.0.7. Django 6 has built-in CSP support, but config/settings.py does not enable it.
Reason for the hardening change
The safe filter disables Django's normal output escaping. If script-capable HTML is stored and published, the browser has no site policy blocking inline JavaScript, event handlers, or scripts loaded from an unapproved origin.
An enforcing CSP can reduce the effect of such a publishing mistake. The missing header is not an exploitable vulnerability by itself, and report-only mode does not block content.
Recommended change
Add Django's CSP middleware to MIDDLEWARE:
"django.middleware.csp.ContentSecurityPolicyMiddleware",
Define the initial policy in SECURE_CSP_REPORT_ONLY. Review at least default-src, script-src, style-src, img-src, font-src, connect-src, object-src, base-uri, form-action, and frame-ancestors.
The policy must account for Google Fonts, the configured R2 media origin, the Mailchimp subscription form, and the scripts used by Django admin, Unfold, and TinyMCE.
Move inline JavaScript to static files where practical. If an inline <script> must remain, add CSP.NONCE to script-src, add django.template.context_processors.csp, and set nonce="{{ csp_nonce }}" on the script element. Replace the onclick handler in the event detail template with an event listener because a script nonce does not authorize inline event handlers.
After testing public, authentication, and admin pages, move the reviewed policy to SECURE_CSP so Django sends an enforcing header. If reports are to be collected, add a reporting directive and configure a receiver.
Continue to sanitize stored HTML according to the elements, attributes, and URL schemes required by each field.
Affected configuration
config/settings.py- Templates containing inline scripts or event handlers
- Templates that render stored HTML with
safe
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in config/settings.py by reviewing the existing MIDDLEWARE and security settings, then inspect the affected templates for inline scripts, event handlers, and external resources. Test public, authentication, and admin pages while accounting for Google Fonts, R2 media, Mailchimp, Unfold, and TinyMCE. Done means the reviewed policy is enforcing and required pages and stored HTML still work.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- django, python
- Domain
- backend, security
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100