ssprkd.io
Security & DevSecOps
Lesson 1 of 6

Web app vulns & the OWASP Top 10

Shut down the web's most-exploited bugs — SQLi, XSS, broken access control, and SSRF — with the durable fixes attackers can't sidestep.

Watch

Speed
📖 Read this walkthrough — every command, and why
OWASP Top 10 (2021) — a defender's checklist

Mental model: this list catalogs the web's most common flaws, and almost every one
traces back to a single mistake — trusting input, or a client, that you never actually
verified. The durable fix for the whole class is the same: push trust back to the server,
validate what comes in, and default to secure. What follows is each risk in one line plus
the DEFENSE. Defensive only — no exploit recipes.

A01 · Broken Access Control
  Risk: the server lets a user reach data or actions they were never authorized for.
  Defense: enforce authorization SERVER-SIDE on every request, and DENY BY DEFAULT.
    - Re-check authz for this exact user against this exact object, every time.
    - A signed token is not proof of authorization. A JWT is signed, not encrypted —
      anyone can base64url-decode its claims and read them:
        payload: {"sub":"alice","role":"user","iss":"sprkd","exp":1893456000}
      The "role":"user" claim is readable by ANYONE and can be forged in transit, so
      never trust it on its word — the signature only proves it wasn't tampered with,
      it does not decide what this user may touch. The server still makes the call.
    - Object-level checks stop IDOR: don't let /api/invoices/1041 -> 1042 return
      another tenant's record just because the id was edited. Ownership is checked
      server-side; obscuring or encrypting the id is NOT a substitute for the check.

A02 · Cryptographic Failures
  Risk: sensitive data left in the clear, or protected with weak or home-grown crypto.
  Defense: TLS everywhere, strong standard algorithms, and NEVER roll your own crypto.
    - Encrypt data in transit (TLS) and at rest; use vetted, current algorithms.
    - No DIY schemes, no weak/legacy ciphers, no hard-coded keys.
    - Never put secrets in a JWT payload — it is readable by anyone who holds the token.

A03 · Injection
  Risk: untrusted input is pasted into a query or command, so the engine can't tell
    your data from its instructions.
  Defense: PARAMETERIZE — bind values as parameters; never concatenate untrusted input.
    Bad (string concatenation — input can alter the query):
        db.query("SELECT * FROM users WHERE email = '" + input + "'")
    Safe (parameterized / prepared — input is bound as a value, never as SQL):
        db.query("SELECT * FROM users WHERE email = ?", [input])
    Why it works: prepared statements send the SQL structure and the data separately,
    so the query's shape is fixed and input can never become executable SQL. Escaping
    quotes and blacklisting keywords like DROP/UNION are brittle and bypassable;
    parameterization removes the injection surface entirely. Same rule for shell
    commands, ORMs, and any other interpreter.
    Related: XSS is the output side of the same idea — it's an output-encoding problem,
    fixed by context-aware encoding of untrusted data where it's rendered (with a strict
    Content-Security-Policy as defense in depth).

A04 · Insecure Design
  Risk: whole classes of threats the design never planned for — no defense was ever built.
  Defense: threat-model EARLY; bake security into the design, not on as an afterthought.
    - Ask "what could go wrong here?" before you build, per feature and trust boundary.
    - Least privilege and secure patterns by default; don't rely on later patching.

A05 · Security Misconfiguration
  Risk: default passwords, open buckets, unneeded features, verbose error messages.
  Defense: ship SECURE DEFAULTS — expose nothing you didn't intend.
    - Change/remove defaults, close unused ports and features, lock down storage.
    - Least privilege; suppress stack traces and internal detail in error responses.

A06 · Vulnerable and Outdated Components
  Risk: outdated libraries and images carrying known, published CVEs.
  Defense: SCAN and PATCH — know what you ship and keep it current.
    - Inventory dependencies (SBOM), scan images/deps for known CVEs, patch on a cadence.
    - Pin images by sha256 digest so what you scanned is what you run.

A07 · Identification and Authentication Failures
  Risk: weak or missing MFA, guessable credentials, sloppy session handling.
  Defense: harden login with MFA and manage sessions correctly.
    - Require multi-factor auth; block weak/breached passwords; rate-limit attempts.
    - Rotate and expire session tokens; invalidate on logout; use HttpOnly, Secure,
      SameSite cookies. (Note: CSRF is a separate risk — a forged authenticated request
      the browser sends automatically — defended by anti-CSRF tokens and SameSite,
      not by output encoding.)

A08 · Software and Data Integrity Failures
  Risk: pipelines and updates that trust unsigned code or data from unverified sources.
  Defense: VERIFY sources and SIGNATURES before you run anything.
    - Check signatures on code, dependencies, and updates (e.g. Sigstore/SLSA).
    - Trust only known registries; verify integrity of anything pulled into the build.

A09 · Security Logging and Monitoring Failures
  Risk: you can't respond to an attack you never saw.
  Defense: LOG security events, monitor them, and ALERT on anomalies.
    - Log authn, authz, and high-value actions with enough context to investigate.
    - Ship logs somewhere tamper-resistant; alert on suspicious patterns; never log secrets.

A10 · Server-Side Request Forgery (SSRF)
  Risk: the server is tricked into calling internal or unintended addresses on an
    attacker's behalf.
  Defense: VALIDATE and ALLOWLIST outbound destinations.
    - Allowlist the hosts the server may call; deny by default.
    - A string denylist (blocking 127.0.0.1, 169.254.169.254) is insufficient — alternate
      encodings, IPv6/decimal forms, DNS rebinding, and redirects slip past it. Resolve
      and pin the address, block private/link-local ranges, disallow redirects, and
      isolate egress.

The recurring root cause: trusting untrusted input.
The recurring defenses: validate input, parameterize, enforce least privilege and
server-side authz, and default to secure. Get those right and you close whole classes
of these bugs at once.

Notes:
  - A JWT is signed, not encrypted — claims are public; the signature only proves the
    token wasn't tampered with. It does not authorize the action; the server must.
  - Parameterize instead of escape/blacklist — the latter are brittle and bypassable.
  - IDOR is broken access control, not a crypto or injection bug — fix it with a
    server-side ownership check, not by hiding or encrypting the identifier.
  - SSRF and injection share a lesson: never trust the input's own claims about itself.

Verify:
  - Decode a JWT's payload and confirm the claims are plainly readable (base64url) —
    proof they must never carry secrets and can't be trusted for authz.
  - Try id-swapping a resource URL against an account that shouldn't own it; the server
    must return 403/404, not the record.
  - Run a dependency/image CVE scan (e.g. trivy) and confirm it flags known-vulnerable
    versions before you ship.