Add API anti-abuse hardening: disable docs in prod, origin validator, exception handler
- Disable OpenAPI docs/redoc/openapi.json when APP_ENV=production - Strip uvicorn Server header with --no-server-header in Dockerfile and docker-compose.yml - Add OriginValidatorMiddleware to reject state-changing requests from disallowed origins - Add global exception handler to prevent stack trace leakage on unhandled errors - Add tests for all new security features (OpenAPI, origin validation, exception handler, server header)
This commit is contained in:
parent
162d9a886d
commit
1ace45353a
8 changed files with 252 additions and 4 deletions
24
api/app.py
24
api/app.py
|
|
@ -13,9 +13,11 @@ from api.rate_limiter import RateLimitMiddleware
|
|||
from api.audit_middleware import AuditLogMiddleware
|
||||
from api.metrics_guard import MetricsGuardMiddleware
|
||||
from api.security_headers import SecurityHeadersMiddleware
|
||||
from api.origin_validator import OriginValidatorMiddleware
|
||||
from dotenv import load_dotenv
|
||||
from fastapi import Depends, FastAPI, HTTPException, Query
|
||||
from fastapi.responses import StreamingResponse
|
||||
from fastapi.responses import JSONResponse, StreamingResponse
|
||||
from starlette.requests import Request
|
||||
from api.auth import User
|
||||
from models.listing import QueryParameters, ListingType, FurnishType
|
||||
from notifications import send_notification
|
||||
|
|
@ -85,7 +87,11 @@ def get_query_parameters(
|
|||
)
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
app = FastAPI(
|
||||
docs_url=None if APP_ENV == "production" else "/docs",
|
||||
redoc_url=None if APP_ENV == "production" else "/redoc",
|
||||
openapi_url=None if APP_ENV == "production" else "/openapi.json",
|
||||
)
|
||||
app.include_router(passkey_router)
|
||||
app.include_router(poi_router)
|
||||
app.mount("/metrics", metrics_app)
|
||||
|
|
@ -108,6 +114,11 @@ app.add_middleware(
|
|||
allow_headers=["Authorization", "Content-Type"],
|
||||
)
|
||||
|
||||
app.add_middleware(
|
||||
OriginValidatorMiddleware,
|
||||
allowed_origins=[*DEV_TIER_ORIGINS, *PROD_TIER_ORIGINS],
|
||||
)
|
||||
|
||||
# Security middleware (added bottom-to-top; last added = outermost)
|
||||
# 3. Rate limiting — enforces per-user limits
|
||||
app.add_middleware(RateLimitMiddleware, config=_rate_limit_config)
|
||||
|
|
@ -119,6 +130,15 @@ app.add_middleware(AuditLogMiddleware)
|
|||
app.add_middleware(SecurityHeadersMiddleware)
|
||||
|
||||
|
||||
@app.exception_handler(Exception)
|
||||
async def unhandled_exception_handler(request: Request, exc: Exception) -> JSONResponse:
|
||||
logger.exception("Unhandled exception")
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content={"detail": "Internal server error"},
|
||||
)
|
||||
|
||||
|
||||
@app.get("/api/status")
|
||||
async def get_status() -> dict[str, str]:
|
||||
request_counter.add(1, {"method": "GET", "path": "/status"})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue