Module 4 · Authentication and security

Lesson 23 — Secrets, rate limiting and GDPR

Secret management, request limits and personal-data protection.

Published
In this lesson
  1. Exercise 1 — Token bucket
  2. Exercise 2 — Secrets
  3. Exercise 3 — GDPR
  4. Exercise 4 — Audit log
  5. Exercise 5 — Minimization
  6. Professor's summary

Exercise 1 — Token bucket

  1. With separate HGETALL/HSET, under concurrency MORE than 5 get through (the threads read the same stale "tokens" and each decides to allow): the classic check-then-act race (03/10/12: the same lesson in another store).
  2. With Lua (or django-ratelimit, which uses it), the script is atomic in Redis: exactly 5 get through and 15 get 429. The pattern: move the check+decrement up to the data server as a single operation.
  3. 429 with Retry-After (seconds or a date) and an RFC 7807 body (type: rate-limited). The proposed limits: hard login (brute force), moderate checkout (bots), generous-but-present public API.

Exercise 2 — Secrets

  1. Trufflehog/gitleaks over the history: with 05's cleanup it should come out clean; if something shows up, the repair is rotating (next item) — deleting the commit fixes nothing.
  2. Dual-rotation runbook: (1) generate the new key at the provider; (2) configure GATEWAY_SECRETS=[new, old] (verification accepts either); (3) deploy; (4) switch signing/redemption usage to the new one; (5) confirm in metrics that nobody uses the old one; (6) remove the old one from the setting and revoke it at the provider. The code that enables it: verification iterates over the list of valid secrets (a single secret = a lock-in of coordinated deployment).
  3. In the orchestrator/KMS (Secrets Manager/Vault): the Docker image is an artifact that gets exported/shared/leaked (public registries, docker save); a secret baked into an image lives in its layers forever. The runtime environment (or a KMS fetch at boot) keeps the secret out of the artifact.

Exercise 3 — GDPR

  1. With low volume (<100 reservations per user): direct JSON in the response is correct (14: idempotent). If the history were huge: 202 + job + signed download (01: 202 Accepted). A decision by expected volume, documented.
  2. Anonymization in transaction.atomic(): email → f"deleted-{uuid}@invalid", name → "Deleted user", state → DELETED, refresh tokens blacklisted, MFA deleted. Reservations/payments intact (fiscal obligation + audit).
  3. MFA: deleted (a dead account doesn't need it). Tokens: revoked (blacklist). Organizer: if they have live events → block automatic deletion and force transferring events to another organizer (or cancellation): the manual support flow is the honest one; 00b's PROTECT FK already signals it.

Exercise 4 — Audit log

  1. Append-only in the app: the model exposes no update/delete and (stronger) at the DB: REVOKE UPDATE, DELETE ON audit_log FROM app_user; — the DB guarantees it even with a code bug.
  2. SELECT * FROM audit_log WHERE action = 'role.change' AND created_at >= '2026-03-01' AND created_at < '2026-04-01' ORDER BY created_at; — with actor and before/after in JSONB: the incident's answer in one query (45 adds req_id).

Exercise 5 — Minimization

  1. Typical in the class project: date_of_birth "just in case" (out: not needed to sell), an unused avatar_url (out or justified), full address for electronic tickets (only if you ship physically). Art. 5: adequacy, minimization — every field with its purpose written down.
  2. Proposed retention: expired PENDING → anonymize at 30 days (the seat returned to the pool long ago); reset tokens → 1h, already in the TTL; access logs → 90 days (security) with long-term anonymized sampling. The purge job (31) queries by date and anonymizes in transactional batches.

Professor's summary

  • Secrets: out of the artifact, dual rotation without downtime, a scanner in CI.
  • Atomic rate limiting in Redis: concurrency is solved where the data lives, once again.
  • The developer's GDPR: minimize, retain with a policy, export and erasure implemented (anonymize ≠ delete the money).
  • Append-only audit log: the difference between "I believe nobody touched anything" and "here is the record".

Closing of the security module. Next: Lesson 24 — SOLID and dependency injection (Architecture module).