diff --git a/server.py b/server.py index 19c14f8..506f7e9 100644 --- a/server.py +++ b/server.py @@ -60,11 +60,29 @@ async def lifespan(app: FastAPI): # Shutdown logger.info("Application shutdown") +# Environment detection +ENVIRONMENT = os.environ.get('ENVIRONMENT', 'development') +IS_PRODUCTION = ENVIRONMENT == 'production' + +# Security: Disable API documentation in production +if IS_PRODUCTION: + print("🔒 Production mode: API documentation disabled") + app_config = { + "lifespan": lifespan, + "root_path": "/membership", + "docs_url": None, # Disable /docs + "redoc_url": None, # Disable /redoc + "openapi_url": None # Disable /openapi.json + } +else: + print("🔓 Development mode: API documentation enabled at /docs and /redoc") + app_config = { + "lifespan": lifespan, + "root_path": "/membership" + } + # Create the main app -app = FastAPI( - lifespan=lifespan, - root_path="/membership" # Configure for serving under /membership path -) +app = FastAPI(**app_config) # Create a router with the /api prefix api_router = APIRouter(prefix="/api") @@ -6262,4 +6280,42 @@ app.add_middleware( allow_headers=["*"], expose_headers=["*"], max_age=600, # Cache preflight requests for 10 minutes -) \ No newline at end of file +) + +# Security Headers Middleware +@app.middleware("http") +async def add_security_headers(request: Request, call_next): + response = await call_next(request) + + # Security headers to protect against common vulnerabilities + security_headers = { + # Prevent clickjacking attacks + "X-Frame-Options": "DENY", + + # Prevent MIME type sniffing + "X-Content-Type-Options": "nosniff", + + # Enable XSS protection in older browsers + "X-XSS-Protection": "1; mode=block", + + # Control referrer information + "Referrer-Policy": "strict-origin-when-cross-origin", + + # Permissions policy (formerly Feature-Policy) + "Permissions-Policy": "geolocation=(), microphone=(), camera=()", + } + + # Add HSTS header in production (force HTTPS) + if IS_PRODUCTION: + security_headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains" + + # Apply all security headers + for header, value in security_headers.items(): + response.headers[header] = value + + # Remove server identification headers + response.headers.pop("Server", None) + + return response + +print(f"✓ Security headers configured (Production: {IS_PRODUCTION})") \ No newline at end of file