Aller au contenu principal
Authentication, encryption, network security, application security, and secure system design.

Security

Authentication, encryption, network security, application security, and secure system design.

Application Security (OWASP & Secure Coding)

The OWASP Top 10: The Threat List

OWASP publishes the Top 10 application security risks — the recurring, high-impact failure classes ranked by real-world prevalence. It’s the shared vocabulary for appsec: “we have an A03 injection risk here” is precise. The current list includes (abridged to the ones every engineer must internalize):

  • A03 Injection — SQL/NoSQL/OS/command injection.
  • A02 Cryptographic failures — no encryption, weak algorithms, hardcoded secrets (the crypto topic’s rules, violated).
  • A01 Broken access control — missing or flawed authorization (dedicated section below).
  • A05 Security misconfiguration — default credentials, verbose errors, missing headers.
  • A07 XSS — cross-site scripting.
  • A06 Vulnerable dependencies — Log4Shell-class supply-chain flaws (dedicated section below).
  • A04 Insecure design, A08 insecure deserialization, A09 logging/monitoring failures, A10 SSRF (dedicated section below).

The Top 10 is a curriculum: walk it and you cover most of what attackers actually exploit.

Input Validation and the Trust Rule

The root of most application vulnerabilities is trusting input. The rule: all input is untrusted until validated — URL params, body fields, headers, uploaded files, cookies, third-party payloads. Two complementary techniques:

  • Allow-listing — validate against what’s allowed (a regex for an email, a set of enum values), not what’s blocked. “Reject '” misses every clever variant; “accept only [0-9a-z-]” has nothing to miss.
  • Output encoding — even validated input must be encoded at the boundary where it’s rendered into another context (HTML, SQL, URL, JSON, shell) so it can’t be interpreted as code.

Validation is about shape (length, type, charset); encoding is about context (where the value gets interpolated). Both are required.

Injection: Never Concatenate Untrusted Data Into a Query

SQL injection happens when untrusted input is concatenated into a query string:

-- The famous bug:
SELECT * FROM users WHERE username = '$name' AND password = '$pass';
-- with $name = "admin' --"  →  WHERE username = 'admin' --' AND password = ''

The fix is parameterized queries (prepared statements): the query template and the data are sent separately, so input can never alter the query structure:

cur.execute("SELECT * FROM users WHERE username = %s AND password = %s", (name, pass))

The same principle applies everywhere input becomes a command: NoSQL injection (don’t build MongoDB queries from unvalidated JSON operators), OS command injection (never shell out with interpolated input), LDAP/template injections. If you’re building a string that contains user data and gets interpreted, you’re probably vulnerable — parameterize or allow-list instead.

XSS: Scripts You Didn’t Write

Cross-site scripting (XSS) injects a script into a page that other users view — so an attacker’s code runs in your users’ browsers, stealing cookies, tokens, and keystrokes. The variants:

  • Reflected — the payload is in the URL and echoed back (“search for <script>...”).
  • Stored — the payload is saved to the database and rendered for every visitor (a comment that runs code for everyone).
  • DOM-based — the payload reaches the DOM through innerHTML/document.write without a server round-trip.

The defenses:

  • Context-aware encoding at the render boundary — the framework’s auto-escaping (React, Vue, server-side templates escape by default). The danger is the deliberate escapes: dangerouslySetInnerHTML, v-html, innerHTML — treat them as a security review flag.
  • Content-Security-Policy (CSP) — the header that tells the browser which sources may execute scripts; a strict CSP (script-src 'self') makes injected inline scripts fail even if encoding was missed. CSP is the safety net under every escaping bug.
  • Validate uploads, sanitize rich content with a safe library (never strip with regex).

CSRF: Commands from Another Site

Cross-Site Request Forgery (CSRF) makes a logged-in user’s browser perform an action the user didn’t intend: the victim visits evil.com, which auto-submits POST /api/transfer?to=attacker&amount=1000 — and the victim’s browser includes their session cookie, so the server can’t tell the request came from another site.

The defenses:

  • SameSite cookies — SameSite=Lax/Strict on the session cookie stops the browser from sending it on cross-site requests. This single header neutralizes most CSRF.
  • CSRF tokens — a per-session, per-form random token the server verifies; the attacker’s cross-site form can’t know it.
  • Custom headers / Origin checks — an API that requires X-Requested-With or verifies the Origin/Referer header.

CSRF is the attack that couples with XSS: if you can inject a script, you can read CSRF tokens — which is why CSP and encoding matter even for APIs.

Authentication Flaws and Session Management

The Auth topic covered the protocols; appsec is about the implementation pitfalls:

  • Broken session handling — session IDs in URLs, not rotated on login, no expiry, predictable IDs. Sessions should be random, HttpOnly+Secure+SameSite, regenerated on privilege change.
  • Account enumeration — login errors that reveal whether the username exists (“unknown user” vs “wrong password”). Use identical error messages and generic timing.
  • Brute force / credential stuffing — rate limiting, lockout (with care), and MFA are the countermeasures.
  • Weak password policies — the modern consensus: length over complexity, breach lists over composition rules, and MFA as the real protection.

Broken Access Control (A01)

The #1 slot on the OWASP Top 10 is not an exotic exploit — it’s the app simply failing to check who is allowed to do what, on every object and every operation. Three shapes it takes:

  • Horizontal privilege escalation — user A reads/modifies user B’s resource. The classic IDOR (Insecure Direct Object Reference): GET /api/order/1234 returns the order without verifying that the caller owns 1234. The object ID is a parameter, not a proof of entitlement.
  • Vertical privilege escalation — a normal user calls an admin endpoint (DELETE /api/users/9) and the server only hid the button in the UI instead of checking the role server-side.
  • Missing checks entirely — the endpoint exists but enforces no authorization at all (authz skipped, or checked only in the client).

The fixes are systematic, not clever:

  • Enforce authorization server-side on every endpoint — never rely on the UI hiding a button.
  • Object-level checks — every GET/PUT/DELETE /api/…/:id must verify the authenticated principal owns or may act on that object (WHERE order.id = ? AND order.owner_id = $currentUser).
  • Default-deny — fail closed: no role/ownership match → 403, not a graceful fallback that leaks data.
  • Avoid exposing raw IDs where possible, but know that obfuscation is not a control — a GUID you can’t guess is still an IDOR if you don’t check ownership.
  • Method-level checks — POST/DELETE/PUT get their own authorization path, not just GET.

A01 is the failure mode that “frontend-only authz” apps have in common with badly designed APIs — and the checklist item (#3) exists because it’s the most common thing reviewers miss.

Server-Side Request Forgery (SSRF, A10)

SSRF exploits the server’s own network access. The app takes a URL from the user — an image proxy, a webhook, a PDF renderer, a file fetch — and makes a request to it from the server. The attacker supplies http://169.254.169.254/latest/meta-data/ (the cloud metadata endpoint) or http://localhost:6379 (an internal Redis), and the server happily fetches what it was never meant to reach. The result: the app’s trust boundary is the perimeter — but the server itself sits inside it, so a single SSRF becomes a pivot into internal networks, secrets, and admin panels.

Why it’s hard to block: a naive allow-list of “public domains” is bypassed with redirects, DNS rebinding, IPv6/IPv4 notation tricks (2130706433), and literal-IP forms. The defenses:

  • Allow-list destinations (scheme, host, port) with explicit denials for link-local/reserved ranges (169.254.0.0/16, 127.0.0.0/8, 0.0.0.0/8, internal subnets, metadata.google.internal).
  • Resolve DNS server-side and re-validate — after resolution, confirm the resolved IP isn’t internal (defeats DNS-rebinding).
  • Don’t follow redirects to unvalidated hosts, or re-validate after each hop.
  • Separate egress — route outbound fetches through a dedicated proxy that can’t reach internal services, and don’t give the fetch path privileged credentials.

Supply-Chain and Vulnerable Dependencies (A06)

Applications run on thousands of lines they didn’t write. A06 Vulnerable and Outdated Components is the class where a library flaw becomes your vulnerability — Log4Shell (a single log4j JNDI lookup vector) and the event-stream/ua-parser-js npm incidents are the canonical wake-up calls. The discipline:

  • SBOM awareness — know what’s in the tree (npm audit, osv-scanner, trivy, pip-audit); you can’t patch what you don’t inventory.
  • Pin and review — lock exact versions, scrutinize new/transferring maintainers, and keep a low-ownership dependency budget.
  • Patch fast, but test — the fix is usually one line; the risk is that upgrading introduces breaking changes — automate so patching isn’t a quarterly event.
  • Runtime mitigation — when a vuln can’t be patched immediately (air-gapped, EOL), apply mitigations at the boundary (e.g. egress filters that block the JNDI callback).
  • Don’t trust the source blindly — a “typosquatted” package (lodash vs lodahs) is a supply-chain attack delivered straight into your build.

Insecure Deserialization

Deserialization turns bytes back into objects (Python pickle, Java ObjectInputStream, PHP unserialize). If the attacker controls the bytes and the class path has a “magic method” that does something dangerous, deserialization of untrusted data can execute arbitrary code — no injection string needed. The rules: never deserialize untrusted data; if you must (message queues, caches), use safe formats (JSON with strict schemas, allow-listed classes), sign/authenticate the payloads, and keep deserialization libraries current.

Security Headers and Secure Defaults

The cheap, high-leverage hardening (every response, every framework):

HeaderWhat it stops
Content-Security-PolicyXSS, injected resources
X-Content-Type-Options: nosniffMIME-sniffing attacks
Strict-Transport-SecurityHTTPS downgrade
X-Frame-Options / frame-ancestorsClickjacking
Referrer-Policyleaking URLs to third parties
SameSite cookiesCSRF

Plus secure defaults as an organizational habit: frameworks that fail closed on authz, secrets out of code (the System Design topic), verbose errors only in dev, and dependency pinning + scanning (the Log4Shell lesson: a vulnerable library is a vulnerable app).

The Secure-Coding Review Checklist

Run this on every change before it ships:

  1. All inputs validated by allow-list; all outputs encoded for their context?
  2. Every query parameterized — no string-built SQL/NoSQL/commands?
  3. Authorization checked on every endpoint (not just the visible ones)? Broken access control is #1 on OWASP for a reason.
  4. No innerHTML/dangerouslySetInnerHTML with untrusted data?
  5. Session cookies HttpOnly+Secure+SameSite; sessions rotate on privilege change?
  6. Security headers set (CSP, nosniff, HSTS, frame-ancestors)?
  7. No secrets in code/config/images/logs?
  8. Dependencies pinned and scanned?
  9. Error messages generic (no stack traces, no account enumeration)?
  10. Rate limiting on auth and sensitive endpoints?

Practice Trajectory

  1. Exploit (in a sandbox) a deliberately vulnerable login query with ' OR 1=1 --, then fix it with parameterization and retest.
  2. Store an XSS payload in a comment field and watch it execute in another browser; then add context encoding and a strict CSP.
  3. Build a CSRF demo and verify SameSite=Lax alone defeats it.
  4. Review a small app against the checklist above and fix the three worst findings.
  5. Exploit an IDOR by changing an object ID in a sandboxed API; fix with a server-side ownership check.
  6. Build a toy SSRF via a URL-fetching endpoint and block it with a destination allow-list; try DNS-rebinding and IPv4-notation bypasses.
  7. Run a dependency scanner and a SAST tool on a repo; triage the results by real exploitability.

When It’s the Right Tool

SituationTakeaway
Any data flows into a queryParameterize — never concatenate
Any data renders in a pageEncode at the boundary + CSP
State-changing requests with cookiesSameSite + CSRF tokens
Login/auth endpointsRate limit + MFA + generic errors
Shipping codeRun the secure-coding checklist