--- phase: 01-core-infrastructure-security plan: 03 type: execute wave: 2 depends_on: ["01-01", "01-02"] files_modified: - backend/app/main.py - backend/app/core/security.py - backend/app/api/deps.py - backend/app/api/v1/endpoints/health.py autonomous: true must_haves: truths: - "Rate limiting blocks requests exceeding 100/minute" - "CSRF tokens are validated on state-changing requests" - "Database connectivity checked in health endpoint" - "Security headers present in responses" artifacts: - path: "backend/app/core/security.py" provides: "Rate limiting and CSRF configuration" contains: "Limiter" - path: "backend/app/api/deps.py" provides: "FastAPI dependency injection" contains: "get_db" - path: "backend/app/main.py" provides: "Security middleware stack" contains: "TrustedHostMiddleware" key_links: - from: "backend/app/main.py" to: "backend/app/core/security.py" via: "limiter import" pattern: "from app\\.core\\.security import" - from: "backend/app/api/v1/endpoints/health.py" to: "backend/app/api/deps.py" via: "Depends(get_db)" pattern: "Depends\\(get_db\\)" --- Implement security middleware stack with rate limiting, CSRF protection, and security headers. Purpose: Protect the API from abuse and common web vulnerabilities (INFR-06, INFR-07). Output: FastAPI application with layered security: rate limiting (100/min), CSRF protection, trusted hosts, CORS, and security headers. @/home/mikkel/.claude/get-shit-done/workflows/execute-plan.md @/home/mikkel/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/01-core-infrastructure-security/01-RESEARCH.md (Pattern 3: FastAPI Security Middleware Stack) @.planning/phases/01-core-infrastructure-security/01-01-SUMMARY.md @.planning/phases/01-core-infrastructure-security/01-02-SUMMARY.md Task 1: Configure rate limiting and CSRF protection backend/app/core/security.py backend/app/api/deps.py Create backend/app/core/security.py: - Import and configure slowapi Limiter: - key_func=get_remote_address - default_limits=["100/minute"] - storage_uri from settings (default to memory, Redis for production) - Configure fastapi-csrf-protect CsrfProtect: - Create CsrfSettings Pydantic model with: - secret_key from settings - cookie_samesite = "lax" - cookie_secure = True (HTTPS only) - cookie_httponly = True - Implement @CsrfProtect.load_config decorator Create backend/app/api/deps.py: - Import get_db from app.db.session - Re-export for cleaner imports in endpoints - Create optional dependency for CSRF validation: ```python async def validate_csrf(csrf_protect: CsrfProtect = Depends()): await csrf_protect.validate_csrf_in_cookies() ``` Run: `cd /home/mikkel/repos/debate && ruff check backend/app/core/security.py backend/app/api/deps.py` Expected: No errors. Rate limiter configured at 100/minute, CSRF protection configured with secure cookie settings. Task 2: Apply security middleware to FastAPI app and update health endpoint backend/app/main.py backend/app/api/v1/endpoints/health.py Update backend/app/main.py with middleware stack (order matters - first added = outermost): 1. TrustedHostMiddleware: - allowed_hosts from settings.allowed_hosts - Block requests with invalid Host header 2. CORSMiddleware: - allow_origins from settings.allowed_origins - allow_credentials=True - allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"] - allow_headers=["*"] - max_age=600 (cache preflight for 10 min) 3. Rate limiting: - app.state.limiter = limiter - Add RateLimitExceeded exception handler 4. Custom middleware for security headers: - Strict-Transport-Security: max-age=31536000; includeSubDomains - X-Content-Type-Options: nosniff - X-Frame-Options: DENY - X-XSS-Protection: 1; mode=block - Referrer-Policy: strict-origin-when-cross-origin Update backend/app/api/v1/endpoints/health.py: - Keep GET /health as simple {"status": "healthy"} - Add GET /health/db that checks database connectivity: - Depends on get_db session - Execute "SELECT 1" query - Return {"status": "healthy", "database": "connected"} on success - Return {"status": "unhealthy", "database": "error", "detail": str(e)} on failure - Add @limiter.exempt decorator to health endpoints (don't rate limit health checks) Start the server and test: ```bash cd /home/mikkel/repos/debate source .venv/bin/activate uvicorn backend.app.main:app --host 0.0.0.0 --port 8000 & sleep 2 # Test health endpoint curl -s http://localhost:8000/health # Test database health curl -s http://localhost:8000/api/v1/health/db # Test security headers curl -sI http://localhost:8000/health | grep -E "(X-Content-Type|X-Frame|Strict-Transport)" # Test rate limiting (make 110 requests) for i in {1..110}; do curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8000/health; done | sort | uniq -c ``` Expected: First 100+ requests return 200, some return 429 (rate limited). Kill the server after testing. Security middleware applied, health endpoints check database, rate limiting blocks excess requests. 1. `curl -sI http://localhost:8000/health` includes security headers (X-Content-Type-Options, X-Frame-Options) 2. `curl http://localhost:8000/api/v1/health/db` returns database status 3. Rapid requests (>100/min) return 429 Too Many Requests 4. Invalid Host header returns 400 Bad Request 5. `ruff check backend/` passes - Rate limiting enforced at 100 requests/minute per IP (INFR-06) - CSRF protection configured (INFR-07) - Security headers present in all responses - Health endpoints verify database connectivity - All middleware applied in correct order After completion, create `.planning/phases/01-core-infrastructure-security/01-03-SUMMARY.md`