Authentication, Rate Limiting and Security
API keys
For server-to-server access, issue API keys:
- Generate long random keys:
secrets.token_urlsafe(32), with a recognisable prefix (pd_live_...). - Store only a hash of the key (like a password); show the full key once at creation.
- Attach scopes (
projects:read,projects:write) and an owner. - Allow revocation and rotation; record
last_used_at.
import hashlib, hmac
def require_api_key(scope):
def deco(view):
@wraps(view)
def wrapped(*a, **kw):
raw = request.headers.get("Authorization", "").removeprefix("Bearer ").strip()
digest = hashlib.sha256(raw.encode()).hexdigest()
key = db.session.scalar(select(ApiKey).filter_by(key_hash=digest, revoked=False))
if not key or scope not in key.scopes:
return api_error(401, "unauthorized", "Invalid or missing API key.")
g.api_user = key.owner
return view(*a, **kw)
return wrapped
return deco
User tokens
For apps acting on behalf of users, use OAuth 2.0 (with PKCE for mobile and single-page apps) or short-lived signed tokens (JWT) plus refresh tokens. Keep access tokens short-lived (minutes), validate signature, expiry, issuer and audience on every request, and never put secrets inside a JWT payload (it is only encoded, not encrypted).
Rate limiting
limiter.limit("100 per minute", key_func=lambda: g.get("api_user_id") or get_remote_address())
Return 429 with Retry-After, and include remaining-quota headers so clients can slow down. Use a shared store (Redis) when running more than one process.
The OWASP API Security Top 10, in practice
| Risk | Defence |
|---|---|
| Broken object-level authorisation (BOLA) | check ownership of every object id in every request |
| Broken authentication | strong tokens, rate-limited auth endpoints, no keys in URLs |
| Broken property-level authorisation | explicit input schemas (no mass assignment) and output serialisers |
| Unrestricted resource consumption | rate limits, pagination caps, payload size limits, timeouts |
| Broken function-level authorisation | separate admin endpoints with role checks |
| Server-side request forgery | never fetch arbitrary user-supplied URLs; allowlist hosts |
| Security misconfiguration | no debug output, strict CORS, security headers |
| Improper inventory | document every endpoint and version; retire old ones |
| Unsafe consumption of other APIs | validate data from third-party APIs too |
BOLA is the most common API vulnerability: GET /v1/invoices/1002 returning another customer's invoice because the code checked only that the user was logged in.
Logging and auditing
Log authentication failures, permission denials and key usage with a request ID; never log full keys or tokens.
Exercise
Add hashed, scoped API keys to your Flask API with a management page to create and revoke them, per-key rate limiting, and tests proving a key without projects:write cannot create projects and that one user's key cannot read another user's private project.
Check yourself
- Why store only a hash of each API key?
- What is BOLA, and how do you prevent it?
- Why must JWT payloads not contain secrets?
Tu progreso se guarda solo en este navegador.
Enviar para revisión (10 puntos)