Files
thrillwiki_django_no_react/backend/config/settings/security.py
pacnpal edcd8f2076 Add secret management guide, client-side performance monitoring, and search accessibility enhancements
- Introduced a comprehensive Secret Management Guide detailing best practices, secret classification, development setup, production management, rotation procedures, and emergency protocols.
- Implemented a client-side performance monitoring script to track various metrics including page load performance, paint metrics, layout shifts, and memory usage.
- Enhanced search accessibility with keyboard navigation support for search results, ensuring compliance with WCAG standards and improving user experience.
2025-12-23 16:41:42 -05:00

193 lines
7.1 KiB
Python

"""
Security configuration for thrillwiki project.
This module configures security headers and settings to protect against common
web vulnerabilities including XSS, clickjacking, MIME sniffing, and more.
Uses python-decouple for consistent environment variable management.
Why python-decouple?
- Already used in base.py for consistency
- Simpler API than django-environ
- Sufficient for our configuration needs
- Better separation of config from code
"""
from decouple import config
# =============================================================================
# Cloudflare Turnstile Configuration
# =============================================================================
# Turnstile is Cloudflare's CAPTCHA alternative for bot protection
# Get keys from: https://dash.cloudflare.com/?to=/:account/turnstile
TURNSTILE_SITE_KEY = config("TURNSTILE_SITE_KEY", default="")
TURNSTILE_SECRET_KEY = config("TURNSTILE_SECRET_KEY", default="")
TURNSTILE_VERIFY_URL = config(
"TURNSTILE_VERIFY_URL",
default="https://challenges.cloudflare.com/turnstile/v0/siteverify",
)
# =============================================================================
# Security Headers Configuration
# =============================================================================
# X-XSS-Protection: Enables browser's built-in XSS filter
# Note: Modern browsers are deprecating this in favor of CSP, but it's still
# useful for older browsers
SECURE_BROWSER_XSS_FILTER = config(
"SECURE_BROWSER_XSS_FILTER", default=True, cast=bool
)
# X-Content-Type-Options: Prevents MIME type sniffing attacks
# When True, adds "X-Content-Type-Options: nosniff" header
SECURE_CONTENT_TYPE_NOSNIFF = config(
"SECURE_CONTENT_TYPE_NOSNIFF", default=True, cast=bool
)
# X-Frame-Options: Protects against clickjacking attacks
# DENY = Never allow framing (most secure)
# SAMEORIGIN = Only allow framing from same origin
X_FRAME_OPTIONS = config("X_FRAME_OPTIONS", default="DENY")
# Referrer-Policy: Controls how much referrer information is sent
# strict-origin-when-cross-origin = Send full URL for same-origin,
# only origin for cross-origin, nothing for downgrade
SECURE_REFERRER_POLICY = config(
"SECURE_REFERRER_POLICY", default="strict-origin-when-cross-origin"
)
# Cross-Origin-Opener-Policy: Prevents cross-origin attacks via window references
# same-origin = Document can only be accessed by windows from same origin
SECURE_CROSS_ORIGIN_OPENER_POLICY = config(
"SECURE_CROSS_ORIGIN_OPENER_POLICY", default="same-origin"
)
# =============================================================================
# HSTS (HTTP Strict Transport Security) Configuration
# =============================================================================
# Include subdomains in HSTS policy
SECURE_HSTS_INCLUDE_SUBDOMAINS = config(
"SECURE_HSTS_INCLUDE_SUBDOMAINS", default=True, cast=bool
)
# HSTS max-age in seconds (31536000 = 1 year, recommended minimum)
SECURE_HSTS_SECONDS = config("SECURE_HSTS_SECONDS", default=31536000, cast=int)
# HSTS preload: Allow inclusion in browser preload lists
# Only enable after confirming HTTPS works properly for all subdomains
SECURE_HSTS_PRELOAD = config("SECURE_HSTS_PRELOAD", default=False, cast=bool)
# URLs exempt from SSL redirect (e.g., health checks)
# Format: comma-separated list of URL patterns
SECURE_REDIRECT_EXEMPT = config(
"SECURE_REDIRECT_EXEMPT",
default="",
cast=lambda v: [s.strip() for s in v.split(",") if s.strip()]
)
# Redirect all HTTP requests to HTTPS
SECURE_SSL_REDIRECT = config("SECURE_SSL_REDIRECT", default=False, cast=bool)
# Header used by proxy to indicate HTTPS
# Common values: ('HTTP_X_FORWARDED_PROTO', 'https')
_proxy_ssl_header = config("SECURE_PROXY_SSL_HEADER", default="")
SECURE_PROXY_SSL_HEADER = (
tuple(_proxy_ssl_header.split(",")) if _proxy_ssl_header else None
)
# =============================================================================
# Session Cookie Security
# =============================================================================
# Only send session cookie over HTTPS
SESSION_COOKIE_SECURE = config("SESSION_COOKIE_SECURE", default=False, cast=bool)
# Prevent JavaScript access to session cookie (mitigates XSS)
SESSION_COOKIE_HTTPONLY = config("SESSION_COOKIE_HTTPONLY", default=True, cast=bool)
# SameSite attribute: Protects against CSRF attacks
# Strict = Cookie only sent for same-site requests (most secure)
# Lax = Cookie sent for same-site and top-level navigations (default)
SESSION_COOKIE_SAMESITE = config("SESSION_COOKIE_SAMESITE", default="Lax")
# =============================================================================
# CSRF Cookie Security
# =============================================================================
# Only send CSRF cookie over HTTPS
CSRF_COOKIE_SECURE = config("CSRF_COOKIE_SECURE", default=False, cast=bool)
# Prevent JavaScript access to CSRF cookie
# Note: Set to False if you need to read the token via JavaScript for AJAX
CSRF_COOKIE_HTTPONLY = config("CSRF_COOKIE_HTTPONLY", default=True, cast=bool)
# SameSite attribute for CSRF cookie
CSRF_COOKIE_SAMESITE = config("CSRF_COOKIE_SAMESITE", default="Lax")
# =============================================================================
# Authentication Backends
# =============================================================================
# Order matters: Django tries each backend in order until one succeeds
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
"allauth.account.auth_backends.AuthenticationBackend",
]
# =============================================================================
# Password Validators
# =============================================================================
# Django's built-in password validators for security
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": (
"django.contrib.auth.password_validation.UserAttributeSimilarityValidator"
),
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
"OPTIONS": {
"min_length": config("PASSWORD_MIN_LENGTH", default=8, cast=int),
},
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# =============================================================================
# Permissions Policy (Feature Policy successor)
# =============================================================================
# Controls which browser features can be used
PERMISSIONS_POLICY = {
"accelerometer": [],
"ambient-light-sensor": [],
"autoplay": [],
"camera": [],
"display-capture": [],
"document-domain": [],
"encrypted-media": [],
"fullscreen": ["self"],
"geolocation": ["self"], # Required for map features
"gyroscope": [],
"interest-cohort": [], # Block FLoC
"magnetometer": [],
"microphone": [],
"midi": [],
"payment": [],
"picture-in-picture": [],
"publickey-credentials-get": [],
"screen-wake-lock": [],
"sync-xhr": [],
"usb": [],
"web-share": ["self"],
"xr-spatial-tracking": [],
}