Reading path: I want to make my app secure

Status: 🟩 COMPLETE Last updated: 2026-06-19 Plain-English tagline: The minimum security baseline every modern webapp needs — what to do, why, and what the threats actually are. Not paranoia; not academic. Just the floor.


What this is

A focused path through the security entries, arranged in the order that makes them MAKE SENSE — threats first (what’s actually trying to happen), defenses second (how each prevents specific attacks). After this path, you can audit a webapp’s security posture systematically instead of relying on luck.

15 stops. Not exhaustive — there’s always more — but covers what would have prevented 95%+ of the real-world breaches you’d want to hear about. For solo / small-team projects, this is the floor; bigger systems add more layers.


How to use this path

Each numbered stop has:

  • 🎯 Why you’re here — what threat or layer this addresses
  • đź“– Read — the entries (with status badges)
  • đź§  Anchor concept — the takeaway worth keeping

Stage 1 — The threat model (3 stops)

You can’t defend against attacks you don’t understand. Start by knowing what attackers actually do.

1. The lay of the land — OWASP Top 10

🎯 Why you’re here: Almost every webapp hack fits into one of about ten categories. Knowing them gives you the vocabulary to think about security systematically.

đź“– Read:

🧠 Anchor concept: The OWASP list updates every few years; the categories barely change. Broken access control, cryptographic failures, injection, insecure design, security misconfig — these have been the top 5 for over a decade. Memorize the SHAPE; details change.


2. The specific attacks worth knowing by name

🎯 Why you’re here: Three attacks come up constantly: XSS, CSRF, SQL injection. Knowing each by name + mechanism is enough to recognize most security questions.

đź“– Read:

🧠 Anchor concept: XSS = attacker injects JavaScript that runs in someone else’s browser. CSRF = attacker tricks a logged-in user’s browser into making a request to your site. SQL injection = attacker manipulates a database query by injecting SQL into a user input. Modern frameworks prevent each by default; the danger is when you opt out.


3. The browser is NOT trusted

🎯 Why you’re here: This is the single most important security principle. Forgetting it produces 90% of real-world breaches.

đź“– Read:

🧠 Anchor concept: The browser runs YOUR code on the USER’S machine — under THEIR control. Anything you put in the browser, the user can read, modify, replay. Authentication checks, price calculations, permission flags — ALL of these must be enforced on the server. The browser is a UI, not a gatekeeper.


Stage 2 — Authentication & sessions (4 stops)

The first defense: knowing who’s making each request. Without this, nothing else works.

4. Authentication vs authorization

🎯 Why you’re here: Two related but distinct concepts. Conflating them produces buggy permission systems.

đź“– Read:

🧠 Anchor concept: Authentication = “proving who you are” (logging in). Authorization = “what you’re allowed to do” (admin vs. regular user). Different code, different concerns. Each request needs BOTH: who is this (auth) and are they allowed to do this (authz).


5. Passwords — never plain, always hashed

🎯 Why you’re here: Storing passwords correctly is non-negotiable. Storing them wrong leads to mass breaches.

đź“– Read:

🧠 Anchor concept: Never store passwords. Store hashes (one-way mathematical transformations) computed with bcrypt, argon2, or scrypt. Even if your database leaks, attackers can’t recover passwords directly. Modern auth services (Supabase Auth, Clerk, Auth0) handle this for you — never roll your own.


6. Sessions, cookies, JWTs

🎯 Why you’re here: After login, how does the server know it’s STILL you on the next request? Sessions and cookies. Understanding the mechanism prevents subtle bugs.

đź“– Read:

đź§  Anchor concept: When you log in, the server sets a cookie containing a session token (or JWT). The browser sends it back on every subsequent request. Cookie flags (Secure, HttpOnly, SameSite) protect against common attacks. Get those flags right; everything else cascades.


7. OAuth and “sign in with…”

🎯 Why you’re here: “Sign in with Google” doesn’t share your Google password with the app. The protocol that makes this safe is OAuth.

đź“– Read:

🧠 Anchor concept: OAuth lets a user authorize an app to access their data WITHOUT sharing their password. The flow involves redirects, tokens, scopes. Supabase Auth wraps OAuth for Google, GitHub, Apple, etc. — you usually never implement OAuth directly; you USE it.


Stage 3 — Defense at the database (2 stops)

If the browser is the attacker’s playground and your code is the gate, the database is the vault. Defense in depth means assuming the gate fails.

8. Row-Level Security (the killer feature)

🎯 Why you’re here: RLS shifts security enforcement to the database itself. Even if your application code has a bug, the database refuses to return data the user shouldn’t see.

đź“– Read:

🧠 Anchor concept: Without RLS: every query in your code needs to filter by user. One missed filter = data leak. With RLS: the database itself enforces “users can only see their own data,” automatically, on every query, no matter which API or function asks. This is what makes Supabase safe for solo projects.


9. SQL injection — what RLS doesn’t prevent

🎯 Why you’re here: Even with RLS, you can still write code that lets attackers run arbitrary SQL. Defense: never concatenate user input into queries.

đź“– Read:

🧠 Anchor concept: Always use parameterized queries — SELECT * FROM users WHERE id = $1 with $1 filled in safely by the driver. NEVER SELECT * FROM users WHERE id = ${userInput} (string interpolation). The Supabase client uses parameterization automatically; raw SQL via rpc() or from(...).select() is safe by default.


Stage 4 — Defense at the network (3 stops)

The user → server connection is hostile by default. Encryption, valid certs, and proper headers harden the channel.

10. HTTPS everywhere

🎯 Why you’re here: Plain HTTP exposes everything in transit. HTTPS is non-negotiable in 2026.

đź“– Read:

🧠 Anchor concept: HTTPS = HTTP + TLS encryption. Vercel auto-issues certificates via Let’s Encrypt. The padlock proves the encryption AND that the certificate is for the right domain. Without HTTPS, anyone on the network reads (or modifies) every request.


11. CORS — the browser’s protection

🎯 Why you’re here: Browsers prevent random sites from making requests to YOUR app on behalf of your logged-in users. The mechanism is called CORS. Understanding it prevents you from accidentally weakening it.

đź“– Read:

🧠 Anchor concept: CORS is a BROWSER-enforced rule. It doesn’t stop a script outside a browser; it stops a malicious WEBSITE from making requests to your API as the user. Don’t set Access-Control-Allow-Origin: * on auth-requiring endpoints; it disables CORS protection.


12. Webhook signatures

🎯 Why you’re here: Webhooks (Stripe, GitHub, etc.) are public endpoints anyone can POST to. Without signature verification, attackers can forge events.

đź“– Read:

đź§  Anchor concept: Every webhook provider signs its payloads. Your endpoint must VERIFY the signature (using HMAC + a shared secret) before processing. Without verification: a public URL accepting unauthenticated POST events. With verification: only the real provider can trigger your handler.


Stage 5 — Defense at the boundary (2 stops)

Trust nothing coming in. Validate everything.

13. Input validation

🎯 Why you’re here: Every byte coming into your server is potentially hostile. Validate shapes, types, lengths, ranges, content — at every boundary.

đź“– Read:

🧠 Anchor concept: Use a schema validator (Zod, Valibot) at every entry point: API routes, server actions, webhook handlers. Anything coming from the browser is unknown until validated. TypeScript types are NOT validation — they only help at compile time.


14. Secrets — keeping the keys safe

🎯 Why you’re here: Your app has API keys, database passwords, service tokens. Mishandling these is the most common security incident.

đź“– Read:

🧠 Anchor concept: Two rules. (1) NEVER commit secrets to git — .env.local is gitignored. (2) NEVER use NEXT_PUBLIC_* for secrets — that prefix exposes the value to every browser visiting your site. Real secrets stay server-only.


Stage 6 — Practice (1 stop)

15. The audit — applying it to a real project

🎯 Why you’re here: Reading entries is one thing; applying them to a specific app is another. Walk through Bible Quest (or your own project) checking each layer.

đź“– Read:

  • Review your app’s:
    • Auth layer — Supabase Auth? Email + password? Social login? Sessions stored where?
    • Database — RLS enabled on every table? Service role key NEVER client-side?
    • API endpoints — Inputs validated with Zod? Output sanitized for HTML contexts?
    • HTTPS — Custom domain has a valid cert? HSTS enabled?
    • Secrets — .env*.local in .gitignore? Vercel env vars set per-environment?
    • Dependencies — npm audit clean? Dependabot enabled on GitHub?
    • Webhooks — Signatures verified before processing?

Run /security-review in Claude Code on your current diff to spot issues automatically.

🧠 Anchor concept: Security isn’t a feature; it’s a layered defense. Every entry on the list above represents a layer. Even if some fail, others catch the threat. The goal is multiple barriers, not perfect first-line defense.


When you finish this path

You’ll be able to:

  • âś… Identify which OWASP category a security question falls into
  • âś… Explain why “the browser can’t be trusted” matters in plain English
  • âś… Audit a project against the 14-point checklist above
  • âś… Recognize when AI-generated code introduces a security hole (and what kind)
  • âś… Know which services (Supabase Auth, Vercel) handle which security concerns for you
  • âś… Set up a new Next.js project with the baseline correctly configured

For a solo developer’s webapp (Bible Quest scale), passing all 14 stops makes you about as secure as is reasonably possible. Larger systems need more (security scanners, penetration tests, formal threat modeling) but the baseline matters most.


What’s NOT in this path

This path is for application security — what YOU do in your code. It doesn’t cover:

  • Infrastructure security — what Vercel, Supabase, AWS do under the hood
  • Compliance — GDPR, HIPAA, SOC 2 (each is its own deep dive)
  • Penetration testing — actively trying to break the app from outside
  • DDoS protection — Cloudflare-level defenses
  • Cryptography internals — how AES, RSA, ECDSA work mathematically

For Bible Quest-scale projects, application security IS the security story. As you grow, the others matter more.


See also


Sources