Security: Add Missing HTTP Security Headers (CSP, HSTS, X-Content-Type-Options)
Problem
It was identified that some critical HTTP security headers are missing or not correctly configured in EJ production responses.
Current Status in src/ej/settings/security.py:
| Header | Status | Current Value | Impact |
|---|---|---|---|
| X-Frame-Options |
|
SAMEORIGIN |
Prevents clickjacking |
| Content-Security-Policy |
|
Empty (env("")) |
Allows XSS attacks |
| Strict-Transport-Security |
|
Not defined | Allows HTTPS downgrade |
| X-Content-Type-Options |
|
Not defined | Allows MIME sniffing |
Evidence
Production verification:
curl -I https://www.ejplatform.org | grep -E "Content-Security-Policy|Strict-Transport-Security|X-Content-Type-Options"
Result: Headers are missing from all responses (confirmed December 3, 2025).
Test with curl on staging/local:
# Local environment
curl -I http://localhost:8000/ | grep -E "CSP|HSTS|X-Content-Type-Options"
# Staging
curl -I https://staging.ejplatform.org/ | grep -E "CSP|HSTS|X-Content-Type-Options"
Security Impact
This vulnerability exposes EJ to multiple attack vectors:
-
Cross-Site Scripting (XSS) - CWE-79
- Severity: High
- Without CSP, attackers can inject arbitrary JavaScript
- Related to: Issue #1505 (Stored/Reflected XSS)
- CVSS: 6.5 (Medium-High)
-
HTTPS Downgrade / Protocol Fallback - CWE-295
- Severity: Medium
- Without HSTS, attackers can downgrade connections to HTTP
- Can lead to man-in-the-middle attacks
- CVSS: 5.9 (Medium)
-
MIME-Sniffing / Content-Type Confusion - CWE-430
- Severity: Medium
- Browsers may interpret content differently than intended
- Can lead to XSS or code injection
- CVSS: 5.3 (Medium)
Combined CVSS Impact: 5.9 (Medium) - Action Item
Root Cause Analysis
The SecurityConf class in src/ej/settings/security.py uses Boogie Configurations framework, which supports:
-
HTTP_*attributes that are automatically converted to HTTP response headers -
env()function for environment-based configuration with fallback defaults
Currently:
-
HTTP_CONTENT_SECURITY_POLICYis defined but empty -
HTTP_STRICT_TRANSPORT_SECURITYis not defined -
HTTP_X_CONTENT_TYPE_OPTIONSis not defined
Proposed Solution
Add the missing security headers to src/ej/settings/security.py by implementing 3 new configurations:
1. Content-Security-Policy (CSP)
Purpose: Prevent XSS injection attacks by controlling which resources can be loaded
Default Value:
default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self';
Configuration:
HTTP_CONTENT_SECURITY_POLICY = env(
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self';",
name="{attr}",
)
2. Strict-Transport-Security (HSTS)
Purpose: Force HTTPS connections and prevent protocol downgrade attacks
Default Value:
max-age=31536000; includeSubDomains; preload
Configuration:
HTTP_STRICT_TRANSPORT_SECURITY = env(
"max-age=31536000; includeSubDomains; preload",
name="{attr}",
)
Explanation of directives:
-
max-age=31536000- Cache policy for 1 year (31536000 seconds) -
includeSubDomains- Apply HSTS to all subdomains -
preload- Enable inclusion in HSTS preload lists (https://hstspreload.org/)
3. X-Content-Type-Options
Purpose: Prevent MIME type sniffing attacks
Default Value:
nosniff
Configuration:
HTTP_X_CONTENT_TYPE_OPTIONS = env(
"nosniff",
name="{attr}",
)
Implementation Details
File to modify: src/ej/settings/security.py
Location: Add the 3 configurations within the SecurityConf class, after the existing HTTP_X_FRAME_OPTIONS definition and before the def finalize(self, settings): method.
Mechanism:
- Boogie Configurations framework automatically converts
HTTP_*attributes to HTTP response headers -
env(default_value, name="{attr}")allows environment variables to override the default - Pattern already used for:
HTTP_X_FRAME_OPTIONS,HTTP_ACCESS_CONTROL_ALLOW_ORIGIN, etc.
Environment variables for customization:
-
HTTP_CONTENT_SECURITY_POLICY- Override default CSP -
HTTP_STRICT_TRANSPORT_SECURITY- Override default HSTS -
HTTP_X_CONTENT_TYPE_OPTIONS- Override default X-Content-Type-Options
Acceptance Criteria
-
Implementation
-
HTTP_CONTENT_SECURITY_POLICYconfigured inSecurityConf -
HTTP_STRICT_TRANSPORT_SECURITYconfigured inSecurityConf -
HTTP_X_CONTENT_TYPE_OPTIONSconfigured inSecurityConf
-
-
Verification
-
Headers returned on all HTTP responses in development -
Headers returned on production: https://www.ejplatform.org -
CSP allows all legitimate EJ resources (no console errors) -
No broken functionality observed
-
-
Testing
-
Verify with: curl -I https://www.ejplatform.org -
Test admin panel functionality -
Test public conversation pages -
Test API endpoints
-
-
Documentation
-
Headers documented in security hardening section -
Configuration options explained in code comments -
Added to Sprint 5 documentation (GCES)
-
References and Standards
Security Standards:
- OWASP Secure Headers Project: https://owasp.org/www-project-secure-headers/
- OWASP Top 10 2021 - A05:2021 – Security Misconfiguration
- Mozilla Web Security Guidelines: https://infosec.mozilla.org/guidelines/web_security
Technical References:
- MDN CSP: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
- MDN HSTS: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
- MDN X-Content-Type-Options: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
- HSTS Preload List: https://hstspreload.org/