Security measures implemented in the GYF backend.
| Feature | Implementation | File |
|---|---|---|
| JWT tokens | HS256 with configurable expiry (default: 7 days) | services/auth_service.py |
| Password hashing | bcrypt via passlib |
services/auth_service.py |
| Token extraction | Bearer token from Authorization header | dependencies.py |
| Secret validation | Rejects default secret in non-dev environments | config.py |
The JWT secret must be set via environment variable in staging/production:
export GYF_ENV=production
export JWT_SECRET=$(openssl rand -base64 32)The app will refuse to start if GYF_ENV != dev and the default secret is used.
| Setting | Default | Configuration |
|---|---|---|
| Requests/minute | 60 | RATE_LIMIT_PER_MINUTE env var |
| Key function | Client IP address | slowapi |
| Response | 429 Too Many Requests |
Automatic |
| Storage | In-memory (use Redis URL for distributed) | rate_limit.py |
All API responses include OWASP-recommended headers:
| Header | Value |
|---|---|
X-Content-Type-Options |
nosniff |
X-Frame-Options |
DENY |
X-XSS-Protection |
1; mode=block |
Referrer-Policy |
strict-origin-when-cross-origin |
Permissions-Policy |
camera=(), microphone=(), geolocation=() |
Strict-Transport-Security |
max-age=31536000; includeSubDomains (HTTPS only) |
CSP is set on the HTML document response via nginx, not on API responses (where it has no security effect and causes browser console warnings):
| Directive | Value |
|---|---|
default-src |
'self' |
script-src |
'self' 'unsafe-inline' 'unsafe-eval' |
style-src |
'self' 'unsafe-inline' |
img-src |
'self' data: blob: https: |
connect-src |
'self' ws: wss: |
font-src |
'self' https://fonts.gstatic.com |
JWT tokens include a role claim (default: user).
# Protect an endpoint:
from backend.app.dependencies import require_role
@router.post("/admin-action", dependencies=[Depends(require_role("admin"))])
async def admin_action(): ...
# Available roles: "user", "admin", "b2b_partner" (extensible)The system supports full data isolation for B2B clients via the TenantContextMiddleware.
- Identity Resolution: Resolves tenant contexts either via
X-API-KEY(B2B requests) orX-Tenant-ID/ Auth Token (D2C/Internal requests). - Data Isolation: SQLAlchemy queries use
apply_tenant_filterto implicitly scopeWHERE tenant_id = X. - API Keys: Managed via
/admin/api-keys, keys are securely verified with SHA-256 caching.
SecurityHeadersMiddleware (outermost — runs last on response)
↓
CORSMiddleware
↓
RateLimiter
↓
PrometheusMiddleware
↓
RequestLoggingMiddleware (innermost — runs first on request)
| Test | What it verifies |
|---|---|
test_security_headers_present |
All OWASP headers on every response |
test_security_headers_no_hsts_on_http |
HSTS only on HTTPS |
test_rbac_unauthorized_no_token |
401 without auth token |
test_jwt_decode_full |
Role claim in JWT |
test_jwt_decode_full_default_role |
Default role is "user" |