Skip to content

Security

Build secure applications with defense in depth.

Security Mindset

Assume Breach

Design systems assuming attackers will get in:

  • Minimize blast radius — Limit what compromised components can access
  • Defense in depth — Multiple layers of protection
  • Least privilege — Only grant necessary permissions
  • Zero trust — Verify everything, trust nothing

Threat Modeling

Before building, ask:

  1. What are we building? — Data flows, trust boundaries
  2. What can go wrong? — STRIDE analysis
  3. What are we doing about it? — Mitigations
  4. Did we do a good job? — Validation
STRIDE:
├── Spoofing        → Authentication
├── Tampering       → Integrity checks
├── Repudiation     → Audit logging
├── Info Disclosure → Encryption, access control
├── Denial of Service → Rate limiting, quotas
└── Elevation of Privilege → Authorization

Section Contents

Topic Description
Secrets Management Environment variables, vaults, rotation
Input Validation Pydantic, Zod, sanitization
Authentication JWT, sessions, MFA
Authorization RBAC, ABAC, permissions
OWASP Top 10 Common vulnerabilities and prevention
Security Checklist Pre-deploy verification

Quick Wins

1. Never Trust User Input

# Bad: Direct use
@app.get("/users/{user_id}")
async def get_user(user_id: str):
    return await db.execute(f"SELECT * FROM users WHERE id = '{user_id}'")

# Good: Parameterized query with validation
@app.get("/users/{user_id}")
async def get_user(user_id: int):  # Type validation
    return await db.execute(
        select(User).where(User.id == user_id)  # ORM parameterizes
    )

2. Use Security Headers

from fastapi import FastAPI
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware

app = FastAPI()

@app.middleware("http")
async def security_headers(request, call_next):
    response = await call_next(request)
    response.headers["X-Content-Type-Options"] = "nosniff"
    response.headers["X-Frame-Options"] = "DENY"
    response.headers["X-XSS-Protection"] = "1; mode=block"
    response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
    response.headers["Content-Security-Policy"] = "default-src 'self'"
    return response

3. Hash Passwords Properly

from passlib.context import CryptContext

pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")

def hash_password(password: str) -> str:
    return pwd_context.hash(password)

def verify_password(plain: str, hashed: str) -> bool:
    return pwd_context.verify(plain, hashed)

4. Validate Everything

from pydantic import BaseModel, EmailStr, Field, field_validator
import re

class UserCreate(BaseModel):
    email: EmailStr
    password: str = Field(..., min_length=12)
    username: str = Field(..., min_length=3, max_length=30)

    @field_validator("username")
    @classmethod
    def username_alphanumeric(cls, v):
        if not re.match(r"^[a-zA-Z0-9_]+$", v):
            raise ValueError("Username must be alphanumeric")
        return v

    @field_validator("password")
    @classmethod
    def password_strength(cls, v):
        if not re.search(r"[A-Z]", v):
            raise ValueError("Password must contain uppercase")
        if not re.search(r"[a-z]", v):
            raise ValueError("Password must contain lowercase")
        if not re.search(r"\d", v):
            raise ValueError("Password must contain digit")
        return v

Security Layers

┌─────────────────────────────────────────┐
│           Network Security              │
│    (Firewall, WAF, DDoS protection)     │
├─────────────────────────────────────────┤
│          Transport Security             │
│         (TLS, HTTPS, HSTS)              │
├─────────────────────────────────────────┤
│        Application Security             │
│  (Auth, input validation, rate limits)  │
├─────────────────────────────────────────┤
│           Data Security                 │
│  (Encryption at rest, access control)   │
├─────────────────────────────────────────┤
│         Monitoring & Response           │
│    (Logging, alerting, incident mgmt)   │
└─────────────────────────────────────────┘

Common Mistakes

Mistake Impact Fix
Secrets in code Credential leak Environment variables, vaults
SQL string concat SQL injection Parameterized queries
No rate limiting DoS, brute force Rate limit all endpoints
Verbose errors Info disclosure Generic error messages
Missing auth checks Unauthorized access Auth on every endpoint
Client-side validation only Bypassed easily Always validate server-side