Skip to content

Proposal: add a site-wide Content-Security-Policy (Report-Only first) #3041

Description

@tonghuaroot

Summary

Right now www.python.org does not send a Content-Security-Policy header (a quick check of the response headers shows only X-Frame-Options: SAMEORIGIN and HSTS). Adding a CSP would give the site meaningful defense-in-depth against XSS: even if user-influenced HTML is ever mis-sanitized, a strict script-src with no unsafe-inline stops injected scripts and javascript: URIs from executing.

This follows up on a request from @sethmlarson to propose a CSP for the python.org service.

Why Report-Only first

python.org serves some inline scripts/styles and pulls assets from a CDN plus a few third-party origins, so a strict policy dropped straight into enforcing mode would break pages. The safe path is to roll out in Content-Security-Policy-Report-Only mode first, collect violation reports, tune the allowlist, and only then switch to enforcing.

Proposed rollout

  1. Add django-csp and enable its middleware.
  2. Wire a per-request nonce into the <script> / <style> tags in the base templates.
  3. Ship a Content-Security-Policy-Report-Only policy with a reporting endpoint and collect violations for a couple of weeks.
  4. Tighten the allowlist from the reports (CDN, analytics, fonts, donate/embed widgets, and so on).
  5. Flip Report-Only to enforcing once the reports are clean.

Starting policy (Report-Only, to be tuned from reports)

default-src 'self';
script-src 'self' 'nonce-{request_nonce}';
style-src 'self' 'nonce-{request_nonce}';
img-src 'self' data:;
font-src 'self';
connect-src 'self';
frame-ancestors 'self';
base-uri 'self';
object-src 'none';
form-action 'self';

This is only a starting point. The exact script-src / style-src / img-src / connect-src origins depend on which third-party services python.org actually loads, which the Report-Only telemetry will surface.

Open questions for maintainers

  • Which third-party origins (CDN, analytics, fonts, embedded widgets, donation flow) need allowlisting?
  • Nonces vs hashes for the existing inline scripts and styles?
  • Preferred reporting endpoint (self-hosted report handler vs an external collector)?

Happy to open a Report-Only PR (django-csp plus nonce wiring and the starting policy above) once the approach looks right.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions