Security¶
Security best practices and configuration.
Security Layers¶
flowchart TB
subgraph "Request"
REQ[HTTP Request]
end
subgraph "Security Stack"
CORS[CORS Check]
HEADERS[Security Headers]
AUTH[Authentication]
PERM[Permissions]
VALID[Input Validation]
end
subgraph "Protection"
XSS[XSS Prevention]
SQLI[SQL Injection Prevention]
CSRF[CSRF Protection]
end
REQ --> CORS --> HEADERS --> AUTH --> PERM --> VALID
VALID --> XSS
VALID --> SQLI
VALID --> CSRF
style CORS fill:#ffcdd2
style AUTH fill:#fff3e0
style PERM fill:#e3f2fd
style VALID fill:#c8e6c9
Security Checklist¶
flowchart LR
subgraph "Must Have"
A[✓ HTTPS]
B[✓ JWT Auth]
C[✓ Input Validation]
D[✓ SQL Parameterization]
end
subgraph "Recommended"
E[✓ Security Headers]
F[✓ Rate Limiting]
G[✓ CORS Config]
H[✓ HSTS]
end
subgraph "Advanced"
I[✓ CSP]
J[✓ Audit Logs]
K[✓ IP Allowlist]
end
style A fill:#c8e6c9
style B fill:#c8e6c9
style C fill:#c8e6c9
style D fill:#c8e6c9
Security Headers¶
Enable via middleware:
# src/settings.py
class AppSettings(Settings):
middleware: list[str] = [
"security_headers",
# ...
]
Default headers:
| Header | Value | Purpose |
|---|---|---|
X-Content-Type-Options |
nosniff |
Prevent MIME sniffing |
X-Frame-Options |
DENY |
Prevent clickjacking |
X-XSS-Protection |
1; mode=block |
XSS filter |
Referrer-Policy |
strict-origin-when-cross-origin |
Control referrer |
Custom Headers¶
middleware = [
("security_headers", {
"headers": {
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "SAMEORIGIN",
"Content-Security-Policy": "default-src 'self'",
},
"enable_hsts": True,
"hsts_max_age": 31536000,
}),
]
HSTS¶
Enable for HTTPS:
middleware = [
("security_headers", {
"enable_hsts": True,
"hsts_max_age": 31536000, # 1 year
}),
]
CORS¶
# src/settings.py
class AppSettings(Settings):
# Allowed origins (empty = none)
cors_origins: list[str] = ["https://app.example.com"]
# Allow credentials (cookies, auth headers)
cors_allow_credentials: bool = True
# Allowed methods
cors_allow_methods: list[str] = ["GET", "POST", "PUT", "PATCH", "DELETE"]
# Allowed headers
cors_allow_headers: list[str] = ["Authorization", "Content-Type"]
Production Warning¶
Never use "*" in production:
# BAD - allows any origin
cors_origins: list[str] = ["*"]
# GOOD - explicit origins
cors_origins: list[str] = [
"https://app.example.com",
"https://admin.example.com",
]
Authentication¶
JWT Configuration¶
class AppSettings(Settings):
secret_key: str = "your-256-bit-secret-key"
access_token_expire_minutes: int = 30
refresh_token_expire_days: int = 7
jwt_algorithm: str = "HS256"
Password Requirements¶
from strider.validators import PasswordValidator
validator = PasswordValidator(
min_length=8,
max_length=128,
require_uppercase=True,
require_lowercase=True,
require_digit=True,
require_special=True,
)
Password Hashing¶
Use strong hashers:
class AppSettings(Settings):
# Recommended: Argon2
password_hasher: str = "argon2" # pip install argon2-cffi
# Alternative: BCrypt
# password_hasher: str = "bcrypt" # pip install bcrypt
SQL Injection Prevention¶
The framework uses SQLAlchemy ORM with parameterized queries:
# SAFE - parameterized query
users = await User.objects.using(db).filter(email=user_input).all()
# SAFE - QuerySet API
users = await User.objects.using(db).filter(
name__contains=search_term
).all()
Never use raw SQL with user input:
# DANGEROUS - never do this
query = f"SELECT * FROM users WHERE email = '{user_input}'"
# SAFE - use parameters
from sqlalchemy import text
result = await db.execute(
text("SELECT * FROM users WHERE email = :email"),
{"email": user_input}
)
Input Validation¶
Use InputSchema¶
from strider.serializers import InputSchema
class UserInput(InputSchema):
email: str
name: str
# Por padrão extra="ignore" (campos desconhecidos ignorados)
# str_strip_whitespace=True limpa strings
# Para rejeitar campos extras: model_config = ConfigDict(extra="forbid")
Validate All Input¶
from strider.validators import (
EmailValidator,
MinLengthValidator,
MaxLengthValidator,
)
class UserViewSet(ModelViewSet):
model = User
field_validators = {
"email": [EmailValidator()],
"name": [MinLengthValidator(2), MaxLengthValidator(100)],
}
Rate Limiting¶
Not built-in. Implement with middleware:
from strider.middleware import ASGIMiddleware
from strider.exceptions import TooManyRequests
class RateLimitMiddleware(ASGIMiddleware):
name = "RateLimitMiddleware"
async def before_request(self, scope, request):
client_ip = request.client.host
if await self.is_rate_limited(client_ip):
raise TooManyRequests(
message="Too many requests",
retry_after=60
)
await self.increment_counter(client_ip)
return None
CSRF Protection¶
Admin panel uses SameSite=Lax cookies:
response.set_cookie(
key="admin_session",
value=session_key,
httponly=True,
samesite="lax",
secure=True, # HTTPS only
)
For API endpoints, use JWT tokens instead of cookies.
Secrets Management¶
Environment Variables¶
# .env (never commit)
SECRET_KEY=your-256-bit-secret-key
DATABASE_URL=postgresql+asyncpg://user:pass@host/db
Never Commit Secrets¶
# .gitignore
.env
.env.local
.env.production
credentials.json
*.pem
*.key
Validate in Production¶
from strider.auth import validate_auth_configuration
issues = validate_auth_configuration()
if issues:
for issue in issues:
print(f"WARNING: {issue}")
Sensitive Data¶
Exclude from Responses¶
class UserOutput(OutputSchema):
id: int
email: str
name: str
# password NOT included
Exclude from Logs¶
middleware = [
("logging", {
"log_body": False, # Don't log request bodies
"log_headers": False, # Don't log headers
}),
]
Production Checklist¶
-
SECRET_KEYis unique and secure (256+ bits) -
DEBUGisFalse -
CORS_ORIGINSis explicit (no"*") - HTTPS is enforced
- HSTS is enabled
- Password hasher is Argon2 or BCrypt
- Rate limiting is implemented
- Sensitive data excluded from logs
-
.envfiles not committed - Database credentials are secure
- Admin panel has strong authentication
Security Headers Reference¶
# Recommended production headers
headers = {
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "strict-origin-when-cross-origin",
"Content-Security-Policy": "default-src 'self'",
"Permissions-Policy": "geolocation=(), microphone=()",
}
Next¶
- Middleware — Request/response hooks
- Auth — Authentication