Chapter 5 · Attacks & Defenses· Advanced· ~24 min

Web Vulnerabilities

Past OWASP Top 10: prototype pollution, CSP bypass classes, request smuggling internals, sandbox escapes.

You will learn

  • How prototype pollution turns "{a:1}" into a server takeover via __proto__
  • The major CSP bypass classes (JSONP, AngularJS sinks, base-uri tricks)
  • HTTP request smuggling at the byte level (CL.TE, TE.CL, TE.TE, HTTP/2 downgrade)
  • iframe sandbox escapes and the documented ways they fail
  • Why supply-chain attacks (npm, build pipelines) eclipse most other web attack classes by impact

Prototype pollution

JavaScript objects inherit from Object.prototype. Code like:

function merge(target, source) {
  for (const k in source) {
    if (typeof source[k] === 'object') merge(target[k] = target[k] || {}, source[k]);
    else target[k] = source[k];
  }
}
merge({}, JSON.parse(userInput));

If userInput is {"__proto__": {"isAdmin": true}}, the merge writes isAdmin: true onto Object.prototype. Every subsequent obj.isAdmin check anywhere in the process returns true.

Real-world impact: 2019 Kibana RCE via prototype pollution into a template renderer. 2022 lodash CVE. The bug class is endemic to libraries that recursively merge user-controlled objects.

Defenses: use Object.create(null) for parsed-input bags, validate keys against an allow-list before merging, prefer Map over plain objects for untrusted key/value data, and ban keys named __proto__, constructor, prototype.

CSP bypass classes

A strict CSP closes most XSS sinks. The bypass classes that survive:

  • JSONP endpoints in allow-listed origins: if your CSP allows https://cdn.example.com and that CDN hosts a JSONP endpoint, an attacker injects <script src="https://cdn.example.com/api?callback=alert"> and gets script execution. The defense is to never list origins that host arbitrary-callback JSONP.
  • AngularJS template injection: if https://ajax.googleapis.com is in script-src and an unescaped attacker string lands inside an Angular-bound DOM tree, the attacker can write Angular template expressions that execute. Defense: drop the Angular CDN from allow-list if you do not use it, or migrate to Angular post-template-injection-fix versions.
  • Dangling base-uri: if you forget base-uri 'none', an attacker who can inject a <base> tag changes the relative-URL resolution for every subsequent <script src="...">, redirecting them to attacker-controlled paths.
  • Strict-dynamic plus a trusted-but-vulnerable script: strict-dynamic propagates trust to scripts loaded by other trusted scripts. If any allow-listed script is exploitable, the attacker chains through it.

HTTP/2 to HTTP/1.1 downgrade smuggling

Front proxies often speak HTTP/2 to clients and HTTP/1.1 to backend origins. HTTP/2 carries headers in binary frames with explicit lengths; HTTP/1.1 uses text framing. The translation between them is where smuggling lives:

  • HTTP/2 forbids the Transfer-Encoding header; some translating proxies passthrough it anyway, recreating CL.TE conditions on the HTTP/1.1 backend.
  • HTTP/2 allows pseudo-headers like :path; bugs in translating these into HTTP/1.1 request-lines have allowed header injection.
  • Even a perfectly compliant translator can be downgraded by injecting CRLF in HTTP/2 header values; the backend sees a request with weird headers.

James Kettle's research (PortSwigger) catalogued real vulnerabilities in major CDNs and load balancers. Defenses: HTTP/2 end-to-end where possible, strict header validation at the translator, reject any HTTP/2 request that carries forbidden HTTP/1.1 headers.

iframe sandbox escapes

The <iframe sandbox> attribute creates a restricted execution context. Each capability re-enabled by a flag (allow-scripts, allow-same-origin, etc.) erodes the protection. The escapes:

  • allow-scripts + allow-same-origin together: the sandbox can remove the sandbox attribute on its own iframe and reload, escaping entirely. Never combine these two flags on untrusted content.
  • postMessage to parent: even fully sandboxed, an iframe can postMessage to its parent. If the parent's onmessage handler does not validate the origin or the data, the iframe can drive the parent.
  • Top-level navigation: without allow-top-navigation the iframe cannot navigate the top frame, but in older browsers there were bugs around window.open with noopener.

Supply chain: the largest blast radius

Most modern web apps depend on thousands of npm/PyPI packages. Single-package compromises (event-stream 2018, ua-parser-js 2021, XZ utils 2024) reach huge user populations. The defense pattern in mature shops:

  • Lockfiles with content hashes for every dependency.
  • Sigstore / npm provenance: signed build metadata tying releases to source commits.
  • Reproducible builds where the build process can be verified independently.
  • Egress-restricted CI: the build cannot exfiltrate secrets to arbitrary destinations.
  • SBOM (Software Bill of Materials) and continuous vulnerability scanning against it.

The pragmatic minimum for any production project: dependabot or renovate, lockfile-only updates, and a policy of declining new transitive dependencies without review.

The bug-class outlook

Memory-unsafe languages drove decades of buffer overflows; memory-safe languages eliminate that bug class (largely). Likewise, parameterized queries eliminate SQL injection, escape-by-default templates eliminate most XSS, and SameSite cookies eliminate most CSRF. The bug classes that remain in 2026:

  • SSRF (cloud metadata).
  • Prototype pollution and other language-quirk injection.
  • Request smuggling.
  • Deserialization gadgets.
  • Supply chain.

The trend is for new bug classes to live higher up the stack (languages, build systems, dependencies) as the lower layers harden. The pattern repeats indefinitely.