Spaces:
Paused
Paused
File size: 2,373 Bytes
7df55e6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | """
security.py
===========
Security layers:
1. CORS β portfolio domain only
2. X-Portfolio-Key β shared secret between Vercel proxy and this backend
(secret lives in Vercel server env, never in browser JS)
3. Rate limiting β slowapi, 10 req/min per IP
"""
import os
import logging
from fastapi import Request, HTTPException
from slowapi import Limiter
from slowapi.util import get_remote_address
logger = logging.getLogger(__name__)
# ββ Secrets βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
PORTFOLIO_SECRET_KEY = os.environ.get("PORTFOLIO_SECRET_KEY", "")
# ββ Allowed origins βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Add your Vercel domain here. localhost entries are for local dev only.
ALLOWED_ORIGINS = [
"https://sulitha-nulaksha-portfolio.vercel.app",
"http://localhost:3000",
"http://localhost:5173",
]
# ββ Rate limiter ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
limiter = Limiter(key_func=get_remote_address, default_limits=["10/minute"])
# ββ Origin token check ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def verify_portfolio_key(request: Request) -> None:
"""
Reject requests that don't carry the correct X-Portfolio-Key header.
This header is set server-side by the Vercel API route proxy β it never
reaches the browser, so it cannot be scraped from frontend JS.
Skipped with a warning if PORTFOLIO_SECRET_KEY is not set (useful for
local dev without the full proxy setup).
"""
if not PORTFOLIO_SECRET_KEY:
logger.warning("PORTFOLIO_SECRET_KEY not set β skipping key check (dev mode).")
return
key = request.headers.get("X-Portfolio-Key", "")
if key != PORTFOLIO_SECRET_KEY:
raise HTTPException(status_code=403, detail="Forbidden")
|