Module 05Term 2 · Security13 poster items

Application Security: OWASP Top 10:2025

Sign in to save your progress across devices. Until then it is kept in this browser.

Why this matters

Security is where AI-generated code fails most often and most expensively. Veracode's 2026 data puts the security pass rate of AI code at about 56%. This is a two-week module and the hardest gate in the programme.

Learning outcomes

  1. Apply the OWASP Top 10:2025 to your own application.
  2. Implement authentication and authorization correctly (OAuth/OIDC, sessions, JWT rotation, IAM, RLS).
  3. Manage secrets so none ever reach code, frontend bundles or logs.
  4. Prevent injection (SQL, XSS, SSRF) and browser attacks (CSRF, CORS misconfiguration).
  5. Defend the perimeter with rate limiting, WAF and DDoS protection.
  6. Run SAST, DAST, secret and dependency scanning in CI.

The OWASP Top 10:2025

ID Risk Typical AI-code failure
A01 Broken Access Control Endpoint checks login but not ownership; RLS off; IDOR via sequential IDs
A02 Security Misconfiguration Debug on, default creds, permissive CORS *, verbose errors, open storage buckets
A03 Software Supply Chain Failures Hallucinated or unpinned packages, compromised dependencies, unsigned builds
A04 Cryptographic Failures Homemade crypto, MD5/SHA1 passwords, secrets in plain text
A05 Injection String-concatenated SQL, dangerouslySetInnerHTML, shell commands with user input
A06 Insecure Design No threat model; business logic abuse (negative quantities, coupon stacking)
A07 Authentication Failures No rate limit on login, weak reset flows, long-lived tokens, no MFA
A08 Software or Data Integrity Failures Unverified webhooks, unsigned updates, insecure deserialisation
A09 Security Logging and Alerting Failures No audit log; no alert on brute force; secrets in logs
A10 Mishandling of Exceptional Conditions Fail-open on errors, swallowed exceptions, stack traces to users

New in 2025: Supply Chain became its own category at A03, and Mishandling of Exceptional Conditions entered at A10. Both are exactly where AI tools are weakest.

Lessons

5.1 Access control (A01, 68)

  • Authentication = who you are. Authorization = what you may do. AI code usually does the first and forgets the second.
  • Check authorization on every request, server-side, for every object (WHERE id = $1 AND org_id = $currentOrg).
  • Deny by default. Roles and permissions in one place (policy module or RLS), not scattered if statements.
  • IAM for infrastructure: each service gets its own identity with least privilege; no shared admin keys; separate dev/staging/prod accounts.
  • Never expose service-role / admin keys to the client. In Supabase, the anon key is public by design, so RLS is your only wall.

5.2 Authentication, OAuth and tokens (69, 70)

  • Do not build auth from scratch. Use a proven provider (Supabase Auth, Clerk, Auth0, WorkOS for enterprise SSO).
  • OAuth 2.0 / OIDC: Authorization Code flow with PKCE for browsers and mobile. Validate state, nonce, issuer, audience.
  • Sessions: HttpOnly, Secure, SameSite=Lax or Strict cookies. Do not keep tokens in localStorage.
  • JWT rotation: short-lived access tokens (5 to 15 minutes), rotating refresh tokens with reuse detection, signing keys rotated via JWKS with key IDs (kid), never alg: none, always verify signature, expiry and audience.
  • Enterprise buyers expect: MFA, SSO (SAML/OIDC), SCIM provisioning, session revocation.

5.3 Secrets management (67)

  • Secrets live in a secrets manager or platform env vars, never in git, never in NEXT_PUBLIC_* / VITE_* variables (those ship to the browser).
  • Pre-commit hook + CI scanning (gitleaks, GitHub secret scanning with push protection).
  • A leaked secret is rotated, not just deleted: git history and forks keep it forever.
  • Separate keys per environment; least-scope API keys (restricted Stripe keys, scoped tokens).
  • Spend caps on every paid API key (LLM, SMS, maps). A leaked key is otherwise a blank cheque.

5.4 Cryptography and encryption (72, A04)

  • Passwords: argon2id or bcrypt via your auth provider. Never hash yourself with SHA.
  • Encryption at rest: managed DB/storage encryption on, backups encrypted. Field-level encryption for high-sensitivity data (national ID numbers, bank details), with keys in a KMS.
  • Use libraries (libsodium, platform KMS); never invent crypto.

5.5 Injection (78, 79, 80)

  • SQL injection: parameterised queries or a query builder/ORM. Never interpolate strings into SQL, including in "raw" escape hatches.
  • XSS: frameworks escape by default; danger zones are dangerouslySetInnerHTML, v-html, rendering markdown or LLM output as HTML. Sanitise with DOMPurify; set a Content-Security-Policy.
  • SSRF: any feature that fetches a user-supplied URL (link previews, webhooks, "import from URL", AI agents browsing) can be pointed at 169.254.169.254 (cloud metadata) or internal services. Allowlist domains, resolve and block private IP ranges, disable redirects or re-check after each, run fetchers in an isolated network.

5.6 Browser security (76, 77)

  • CORS is not a security boundary for your server; it controls which origins browsers let read responses. Never reflect arbitrary Origin with Access-Control-Allow-Credentials: true. Allowlist exact origins.
  • CSRF: SameSite cookies plus CSRF tokens or origin checks for state-changing requests when using cookie auth.
  • Security headers: CSP, HSTS, X-Content-Type-Options: nosniff, Referrer-Policy, frame-ancestors.

5.7 Perimeter: rate limiting, WAF, DDoS (1, 74, 75)

  • Rate limit by IP, user and API key, with tighter limits on login, sign-up, password reset, OTP, and every endpoint that costs money (LLM, SMS, email).
  • Algorithms: token bucket or sliding window, stored in Redis or at the edge. Return 429 with Retry-After.
  • WAF (Cloudflare, AWS WAF) with managed OWASP rules; bot protection on sign-up.
  • DDoS protection comes largely from your CDN; make sure the origin is not directly reachable and bypassable.

5.8 Exceptional conditions (A10)

  • Fail closed: if the auth check throws, deny. If the fraud check times out, hold the transaction, do not approve it.
  • Never show stack traces to users; log them with a request ID.
  • Catch specific errors; do not swallow all exceptions.

5.9 Threat modelling (A06)

STRIDE in 30 minutes per feature: Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege. Draw trust boundaries; list abuse cases ("what if quantity is -1?").

5.10 Security in the pipeline

Tool type Example Catches
Secret scan gitleaks, GitHub push protection Keys in code
SAST Semgrep, CodeQL Injection, unsafe patterns
SCA Dependabot, Snyk, OSV-Scanner Vulnerable dependencies
DAST OWASP ZAP Runtime misconfig, headers, XSS
Container/IaC Trivy, Checkov Bad images and infra config

Lab 5 (two weeks): Attack your own app

Week A, Break: use a second account and a browser proxy (ZAP or Burp Community).

  1. Try to read/edit another user's records by changing IDs (IDOR).
  2. Call the database directly with the public anon key.
  3. Inject <script> into every text field and markdown/LLM output.
  4. Point any URL-fetching feature at http://169.254.169.254/ and http://localhost.
  5. Brute force login and password reset; hammer the most expensive endpoint.
  6. Search the production JS bundle for secrets.

Week B, Fix and prove: fix every finding, add a regression test per finding, wire scanners into CI.

Definition of Done

AI prompt pack

  • "Act as a penetration tester. For each route in this app, list how an authenticated user could access another user's data. Then write tests for each."
  • "Find every place secrets or env vars are read. Flag any that could reach the client bundle."
  • "Find every place we fetch a URL derived from user input. Add SSRF protection that blocks private, loopback and link-local ranges after DNS resolution."
  • "Find every catch block. Flag any that fail open or swallow errors."