mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-23 02:31:08 -05:00
feat: Refactor rides app with unique constraints, mixins, and enhanced documentation
- Added migration to convert unique_together constraints to UniqueConstraint for RideModel. - Introduced RideFormMixin for handling entity suggestions in ride forms. - Created comprehensive code standards documentation outlining formatting, docstring requirements, complexity guidelines, and testing requirements. - Established error handling guidelines with a structured exception hierarchy and best practices for API and view error handling. - Documented view pattern guidelines, emphasizing the use of CBVs, FBVs, and ViewSets with examples. - Implemented a benchmarking script for query performance analysis and optimization. - Developed security documentation detailing measures, configurations, and a security checklist. - Compiled a database optimization guide covering indexing strategies, query optimization patterns, and computed fields.
This commit is contained in:
@@ -115,6 +115,8 @@ MIDDLEWARE = [
|
||||
"django.middleware.cache.UpdateCacheMiddleware",
|
||||
"corsheaders.middleware.CorsMiddleware", # CORS middleware for API
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"apps.core.middleware.security_headers.SecurityHeadersMiddleware", # Custom security headers (CSP, Permissions-Policy)
|
||||
"apps.core.middleware.rate_limiting.AuthRateLimitMiddleware", # Rate limiting for auth endpoints
|
||||
"whitenoise.middleware.WhiteNoiseMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
@@ -471,50 +473,91 @@ CACHES = {
|
||||
}
|
||||
|
||||
# Use Redis for sessions
|
||||
# =============================================================================
|
||||
# Session Security Settings
|
||||
# =============================================================================
|
||||
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
|
||||
SESSION_CACHE_ALIAS = "sessions"
|
||||
SESSION_COOKIE_AGE = 86400 # 24 hours
|
||||
SESSION_COOKIE_AGE = 3600 # 1 hour (reduced from 24 hours for security)
|
||||
SESSION_SAVE_EVERY_REQUEST = True # Update session on each request (sliding expiry)
|
||||
SESSION_COOKIE_HTTPONLY = True # Prevent JavaScript access to session cookie
|
||||
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # Session persists until cookie expires
|
||||
|
||||
# Cache middleware settings
|
||||
CACHE_MIDDLEWARE_SECONDS = 300 # 5 minutes
|
||||
CACHE_MIDDLEWARE_KEY_PREFIX = "thrillwiki"
|
||||
|
||||
# =============================================================================
|
||||
# JWT Settings
|
||||
# =============================================================================
|
||||
# Security considerations:
|
||||
# - Short access token lifetime reduces window of vulnerability
|
||||
# - Refresh token rotation prevents token reuse after refresh
|
||||
# - Token blacklisting allows revocation of compromised tokens
|
||||
# - JTI claim enables unique token identification for logging
|
||||
|
||||
SIMPLE_JWT = {
|
||||
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=60), # 1 hour
|
||||
# Token lifetimes
|
||||
# Security: Shorter access tokens (15 min) provide better security
|
||||
# but may require more frequent refreshes
|
||||
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=15), # 15 minutes (reduced from 60)
|
||||
"REFRESH_TOKEN_LIFETIME": timedelta(days=7), # 7 days
|
||||
|
||||
# Token rotation and blacklisting
|
||||
# Security: Rotate refresh tokens on each use and blacklist old ones
|
||||
"ROTATE_REFRESH_TOKENS": True,
|
||||
"BLACKLIST_AFTER_ROTATION": True,
|
||||
|
||||
# Update last login on token refresh
|
||||
"UPDATE_LAST_LOGIN": True,
|
||||
|
||||
# Cryptographic settings
|
||||
"ALGORITHM": "HS256",
|
||||
"SIGNING_KEY": SECRET_KEY,
|
||||
"VERIFYING_KEY": None,
|
||||
|
||||
# Token validation
|
||||
"AUDIENCE": None,
|
||||
"ISSUER": None,
|
||||
"ISSUER": "thrillwiki", # Added issuer for token validation
|
||||
"JWK_URL": None,
|
||||
"LEEWAY": 0,
|
||||
"LEEWAY": 0, # No leeway for token expiration
|
||||
|
||||
# Authentication header
|
||||
"AUTH_HEADER_TYPES": ("Bearer",),
|
||||
"AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
|
||||
|
||||
# User identification
|
||||
"USER_ID_FIELD": "id",
|
||||
"USER_ID_CLAIM": "user_id",
|
||||
"USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",
|
||||
|
||||
# Token classes
|
||||
"AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
|
||||
"TOKEN_TYPE_CLAIM": "token_type",
|
||||
"TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",
|
||||
|
||||
# JTI claim for unique token identification
|
||||
# Security: Enables token tracking and revocation
|
||||
"JTI_CLAIM": "jti",
|
||||
|
||||
# Sliding token settings (if using sliding tokens)
|
||||
"SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
|
||||
"SLIDING_TOKEN_LIFETIME": timedelta(minutes=60),
|
||||
"SLIDING_TOKEN_LIFETIME": timedelta(minutes=15),
|
||||
"SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),
|
||||
}
|
||||
|
||||
# dj-rest-auth settings
|
||||
# =============================================================================
|
||||
# dj-rest-auth Settings
|
||||
# =============================================================================
|
||||
REST_AUTH = {
|
||||
"USE_JWT": True,
|
||||
"JWT_AUTH_COOKIE": "thrillwiki-auth",
|
||||
"JWT_AUTH_REFRESH_COOKIE": "thrillwiki-refresh",
|
||||
"JWT_AUTH_SECURE": not DEBUG, # Use secure cookies in production
|
||||
# Security: Only send cookies over HTTPS in production
|
||||
"JWT_AUTH_SECURE": not DEBUG,
|
||||
# Security: Prevent JavaScript access to cookies
|
||||
"JWT_AUTH_HTTPONLY": True,
|
||||
# Security: SameSite cookie attribute (Lax is compatible with OAuth flows)
|
||||
"JWT_AUTH_SAMESITE": "Lax",
|
||||
"JWT_AUTH_RETURN_EXPIRATION": True,
|
||||
"JWT_TOKEN_CLAIMS_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",
|
||||
|
||||
@@ -24,14 +24,32 @@ ALLOWED_HOSTS = base.config("ALLOWED_HOSTS")
|
||||
# CSRF trusted origins for production
|
||||
CSRF_TRUSTED_ORIGINS = base.config("CSRF_TRUSTED_ORIGINS")
|
||||
|
||||
# Security settings for production
|
||||
# =============================================================================
|
||||
# Security Settings for Production
|
||||
# =============================================================================
|
||||
|
||||
# SSL/HTTPS enforcement
|
||||
SECURE_SSL_REDIRECT = True
|
||||
SESSION_COOKIE_SECURE = True
|
||||
CSRF_COOKIE_SECURE = True
|
||||
|
||||
# HSTS (HTTP Strict Transport Security)
|
||||
SECURE_HSTS_SECONDS = 31536000 # 1 year
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
||||
SECURE_HSTS_PRELOAD = True
|
||||
|
||||
# Session cookie security (stricter than development)
|
||||
SESSION_COOKIE_SECURE = True # Only send over HTTPS
|
||||
SESSION_COOKIE_SAMESITE = "Strict" # Stricter than Lax for production
|
||||
|
||||
# CSRF cookie security (stricter than development)
|
||||
CSRF_COOKIE_SECURE = True # Only send over HTTPS
|
||||
CSRF_COOKIE_SAMESITE = "Strict" # Stricter than Lax for production
|
||||
|
||||
# Additional security headers
|
||||
X_FRAME_OPTIONS = "DENY" # Never allow framing
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||
SECURE_REFERRER_POLICY = "strict-origin-when-cross-origin"
|
||||
SECURE_CROSS_ORIGIN_OPENER_POLICY = "same-origin"
|
||||
|
||||
# Production logging
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
import environ
|
||||
@@ -14,23 +17,136 @@ TURNSTILE_VERIFY_URL = env(
|
||||
default="https://challenges.cloudflare.com/turnstile/v0/siteverify",
|
||||
)
|
||||
|
||||
# Security headers and settings (for production)
|
||||
# =============================================================================
|
||||
# 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 = env.bool("SECURE_BROWSER_XSS_FILTER", default=True)
|
||||
|
||||
# X-Content-Type-Options: Prevents MIME type sniffing attacks
|
||||
# When True, adds "X-Content-Type-Options: nosniff" header
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = env.bool("SECURE_CONTENT_TYPE_NOSNIFF", default=True)
|
||||
|
||||
# X-Frame-Options: Protects against clickjacking attacks
|
||||
# DENY = Never allow framing (most secure)
|
||||
# SAMEORIGIN = Only allow framing from same origin
|
||||
X_FRAME_OPTIONS = env("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 = env(
|
||||
"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 = env(
|
||||
"SECURE_CROSS_ORIGIN_OPENER_POLICY", default="same-origin"
|
||||
)
|
||||
|
||||
# =============================================================================
|
||||
# HSTS (HTTP Strict Transport Security) Configuration
|
||||
# =============================================================================
|
||||
|
||||
# Include subdomains in HSTS policy
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool(
|
||||
"SECURE_HSTS_INCLUDE_SUBDOMAINS", default=True
|
||||
)
|
||||
SECURE_HSTS_SECONDS = env.int("SECURE_HSTS_SECONDS", default=31536000) # 1 year
|
||||
|
||||
# HSTS max-age in seconds (31536000 = 1 year, recommended minimum)
|
||||
SECURE_HSTS_SECONDS = env.int("SECURE_HSTS_SECONDS", default=31536000)
|
||||
|
||||
# HSTS preload: Allow inclusion in browser preload lists
|
||||
# Only enable after confirming HTTPS works properly for all subdomains
|
||||
SECURE_HSTS_PRELOAD = env.bool("SECURE_HSTS_PRELOAD", default=False)
|
||||
|
||||
# URLs exempt from SSL redirect (e.g., health checks)
|
||||
SECURE_REDIRECT_EXEMPT = env.list("SECURE_REDIRECT_EXEMPT", default=[])
|
||||
|
||||
# Redirect all HTTP requests to HTTPS
|
||||
SECURE_SSL_REDIRECT = env.bool("SECURE_SSL_REDIRECT", default=False)
|
||||
|
||||
# Header used by proxy to indicate HTTPS (e.g., ('HTTP_X_FORWARDED_PROTO', 'https'))
|
||||
SECURE_PROXY_SSL_HEADER = env.tuple("SECURE_PROXY_SSL_HEADER", default=None)
|
||||
|
||||
# Session security
|
||||
# =============================================================================
|
||||
# Session Cookie Security
|
||||
# =============================================================================
|
||||
|
||||
# Only send session cookie over HTTPS
|
||||
SESSION_COOKIE_SECURE = env.bool("SESSION_COOKIE_SECURE", default=False)
|
||||
|
||||
# Prevent JavaScript access to session cookie (mitigates XSS)
|
||||
SESSION_COOKIE_HTTPONLY = env.bool("SESSION_COOKIE_HTTPONLY", default=True)
|
||||
|
||||
# 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 = env("SESSION_COOKIE_SAMESITE", default="Lax")
|
||||
|
||||
# CSRF security
|
||||
# =============================================================================
|
||||
# CSRF Cookie Security
|
||||
# =============================================================================
|
||||
|
||||
# Only send CSRF cookie over HTTPS
|
||||
CSRF_COOKIE_SECURE = env.bool("CSRF_COOKIE_SECURE", default=False)
|
||||
|
||||
# Prevent JavaScript access to CSRF cookie
|
||||
# Note: Set to False if you need to read the token via JavaScript for AJAX
|
||||
CSRF_COOKIE_HTTPONLY = env.bool("CSRF_COOKIE_HTTPONLY", default=True)
|
||||
|
||||
# SameSite attribute for CSRF cookie
|
||||
CSRF_COOKIE_SAMESITE = env("CSRF_COOKIE_SAMESITE", default="Lax")
|
||||
|
||||
# =============================================================================
|
||||
# File Upload Security
|
||||
# =============================================================================
|
||||
|
||||
# Maximum size (in bytes) of file to upload into memory (2.5MB)
|
||||
FILE_UPLOAD_MAX_MEMORY_SIZE = env.int(
|
||||
"FILE_UPLOAD_MAX_MEMORY_SIZE", default=2621440
|
||||
)
|
||||
|
||||
# Maximum size (in bytes) of request data (10MB)
|
||||
DATA_UPLOAD_MAX_MEMORY_SIZE = env.int(
|
||||
"DATA_UPLOAD_MAX_MEMORY_SIZE", default=10485760
|
||||
)
|
||||
|
||||
# File upload permissions (0o644 = rw-r--r--)
|
||||
FILE_UPLOAD_PERMISSIONS = 0o644
|
||||
|
||||
# Directory permissions for uploaded files (0o755 = rwxr-xr-x)
|
||||
FILE_UPLOAD_DIRECTORY_PERMISSIONS = 0o755
|
||||
|
||||
# =============================================================================
|
||||
# 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": [],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user