This document explains the security features implemented in the Investment Helper API.
- Location: app/core/security.py, app/core/dependencies.py
- Features:
- Access tokens (30-minute expiry)
- Refresh tokens (7-day expiry)
- BCrypt password hashing
- Token type validation
- HTTPBearer scheme
Usage in routes:
from app.core.dependencies import get_current_active_user
@router.get("/protected")
async def protected_route(current_user: dict = Depends(get_current_active_user)):
# Only authenticated users can access
return {"user": current_user}- Location: app/schemas/etf.py
- Features:
- Pydantic models for all inputs
- Type checking
- Length constraints
- Regex patterns
- Custom validators
- Range validation
Example:
ticker: str = Field(
...,
min_length=1,
max_length=10,
pattern="^[A-Z]{1,10}$"
)- Location: app/core/middleware.py
- Headers added:
X-Content-Type-Options: nosniffX-Frame-Options: DENYX-XSS-Protection: 1; mode=blockStrict-Transport-SecurityContent-Security-PolicyReferrer-Policy
- Location: app/core/middleware.py
- Default: 60 requests per minute per IP
- Features:
- Per-IP tracking
- Sliding window
- Rate limit headers in response
- Configurable limits
- Location: app/main.py
- Features:
- Explicit origin whitelist
- Specific allowed methods
- Specific allowed headers
- Credentials support
- Features:
- Never expose internal errors
- Generic error messages to clients
- Detailed logging server-side
- Prevent information leakage
- DynamoDB/PynamoDB prevents:
- SQL injection (NoSQL database)
- Parameterized queries
- Type-safe operations
pip install -r requirements.txtcp backend/.env.example backend/.envEdit .env and set:
# Generate a secure secret key
SECRET_KEY=$(openssl rand -hex 32)- Configure CORS origins in
.env - Set appropriate rate limits
- Configure AWS credentials
Follow this pattern for all routers:
from fastapi import APIRouter, Depends, Query
from app.core.dependencies import get_current_active_user
from app.schemas.your_schema import YourResponse, ErrorResponse
router = APIRouter(
prefix="/your-endpoint",
tags=["Your Tag"],
responses={
401: {"model": ErrorResponse},
403: {"model": ErrorResponse},
404: {"model": ErrorResponse},
}
)
@router.get(
"/{id}",
response_model=YourResponse,
summary="Short description",
description="Long description"
)
async def get_item(
id: str = Query(..., min_length=1, max_length=50),
current_user: dict = Depends(get_current_active_user)
):
"""
Detailed docstring
"""
try:
# Your logic here
return result
except Exception as e:
# Log internally
print(f"Error: {str(e)}")
# Return generic error
raise HTTPException(500, "Internal error")from app.core.security import create_access_token
token = create_access_token(
data={
"sub": "user123",
"email": "user@example.com",
"username": "testuser"
}
)# Get a token first (you'll need to implement a /login endpoint)
TOKEN="your-jwt-token"
# Make authenticated request
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/api/v1/etfs/SPYBefore deploying to production:
- Change
SECRET_KEYto a strong random value - Set
reload=Falsein uvicorn - Configure proper CORS origins (no wildcards)
- Enable HTTPS/TLS
- Set up proper logging and monitoring
- Implement user authentication endpoint
- Add database user verification
- Consider Redis for rate limiting (not in-memory)
- Set up API key rotation
- Configure WAF rules
- Enable audit logging
- Implement input sanitization for file uploads
- Add request size limits
- Set up security scanning in CI/CD
- Production Rate Limiting: Use Redis instead of in-memory
- Database: Implement row-level security
- Logging: Use structured logging (JSON format)
- Monitoring: Set up alerts for security events
- Backups: Regular encrypted backups
- Dependencies: Regular security updates (
pip-audit) - Secrets: Use AWS Secrets Manager or similar
- TLS: Use TLS 1.3 minimum