P0.5: Database Row-Level Security (RLS) - CRITICAL - Created Alembic migration for RLS policies on all client-scoped tables - Policies for: presentations, master_decks, brand_configs, slides, templates - Updated get_async_session to set PostgreSQL session variables - Multi-tenant isolation now enforced at database level (defense-in-depth) - Session variables: app.current_user_id, app.user_role P0.6: Safe Error Messages - Created safe_exception_handler to prevent info disclosure - Logs full errors internally with context (user_id, path, method) - Returns generic "internal error" message to clients - Preserves HTTPException details (intentional error messages) P0.7: Security Headers - Created SecurityHeadersMiddleware with comprehensive headers - Headers: X-Content-Type-Options, X-Frame-Options, X-XSS-Protection - CSP, Referrer-Policy, Permissions-Policy, HSTS - Updated nginx.conf with matching security headers P0.8: Database Connection Pool Optimization - Increased pool_size from 5 to 20 connections - Added max_overflow of 40 for burst traffic - Enabled pool_pre_ping for connection health checks - Pool recycle after 1 hour to prevent stale connections - Configurable via DB_POOL_SIZE, DB_MAX_OVERFLOW, DB_POOL_RECYCLE All critical pre-launch security tasks complete. System now has: ✅ CORS protection ✅ Rate limiting ✅ Request size limits ✅ Database-level tenant isolation (RLS) ✅ Safe error handling ✅ Security headers ✅ Optimized connection pooling Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
60 lines
2 KiB
Python
60 lines
2 KiB
Python
"""
|
|
Security headers middleware.
|
|
|
|
Adds HTTP security headers to all responses:
|
|
- X-Content-Type-Options: Prevent MIME sniffing
|
|
- X-Frame-Options: Prevent clickjacking
|
|
- X-XSS-Protection: Enable XSS filter
|
|
- Strict-Transport-Security: Force HTTPS
|
|
- Content-Security-Policy: Restrict resource loading
|
|
"""
|
|
|
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
|
|
|
|
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
|
|
"""Middleware that adds security headers to all responses."""
|
|
|
|
async def dispatch(self, request, call_next):
|
|
response = await call_next(request)
|
|
|
|
# Prevent MIME type sniffing
|
|
response.headers["X-Content-Type-Options"] = "nosniff"
|
|
|
|
# Prevent clickjacking
|
|
response.headers["X-Frame-Options"] = "DENY"
|
|
|
|
# Enable XSS protection (legacy, but still useful for older browsers)
|
|
response.headers["X-XSS-Protection"] = "1; mode=block"
|
|
|
|
# Force HTTPS (only if not in dev mode)
|
|
# Remove this in development if using HTTP
|
|
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
|
|
|
|
# Content Security Policy
|
|
# Note: 'unsafe-inline' and 'unsafe-eval' needed for React and dynamic content
|
|
# Tighten these in production if possible
|
|
response.headers["Content-Security-Policy"] = (
|
|
"default-src 'self'; "
|
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval'; "
|
|
"style-src 'self' 'unsafe-inline'; "
|
|
"img-src 'self' data: https:; "
|
|
"font-src 'self' data:; "
|
|
"connect-src 'self'; "
|
|
"frame-ancestors 'none';"
|
|
)
|
|
|
|
# Referrer policy
|
|
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
|
|
|
|
# Permissions policy (restrict browser features)
|
|
response.headers["Permissions-Policy"] = (
|
|
"geolocation=(), "
|
|
"microphone=(), "
|
|
"camera=(), "
|
|
"payment=(), "
|
|
"usb=(), "
|
|
"magnetometer=()"
|
|
)
|
|
|
|
return response
|