Part 12 · 2 chapters · ~12 min

Security

From the browser's trust boundary to the controls that hold it: origins and the same-origin policy, XSS and CSP, CSRF and clickjacking, the supply chain, sessions and identity, and isolation.

26

Boundaries, injection and the supply chain

The browser's trust model, the attacks that break it, and the controls in layers.

Origin

vs https://app.ex.comhttps://app.ex.comsame originhttp://app.ex.comdifferent schemehttps://api.ex.comdifferent hosthttps://app.ex.com:8443different port

Scheme, host and port together; the browser's unit of trust: scripts can read only their own origin's data unless another opts in.

in practicehttps://app.example.com:443; why api.example.com is a different origin from app.example.com.

deep dive: Browser P9

Same-origin policy

same originread DOMread responsesread storagecross originembed: yesread: only via CORSpostMessage to talk

The rule that a script may freely read responses, DOM and storage only from its own origin; everything cross-origin needs an explicit opt-in.

in practiceThe foundation under CORS, postMessage and iframes; the reason XSS is so damaging (it runs as your origin).

deep dive: Browser P9

Site vs origin

app.ex.com → api.ex.comsame site, cross originex.com → other.comcross sitea.github.io → b.github.iocross site (public suffix)

A site is the registrable domain plus scheme (https://example.com); several origins can share one site, which matters for cookies and SameSite.

in practiceSameSite=Lax treating app.example.com and api.example.com as same-site; the Public Suffix List.

deep dive: Browser P9

XSS

attacker inp…stored or reflec…sinkinnerHTML, evalruns as youcookies, DOM, AP…

Cross-site scripting: attacker-controlled data executed as script in your origin, where it can read the page, call APIs as the user and steal tokens.

in practiceinnerHTML with user content, javascript: URLs, unsafe markdown rendering.

deep dive: Disciplines P6

CSRF

evil.combrowserbank.comauto-submit formPOST /transfer + cookieSameSite blocks it

Cross-site request forgery: another site making the user's browser send an authenticated request to yours, riding on their cookies.

in practiceSameSite cookies as the main defence; CSRF tokens for state-changing forms.

deep dive: Browser P9

Clickjacking

decoy page "Win a prize"visi…your page in iframeopac…"Confirm transfer"unde…

Framing your page invisibly under a decoy so a user's click lands on your button.

in practiceframe-ancestors in CSP or X-Frame-Options: DENY on every authenticated page.

deep dive: Disciplines P6

Content Security Policy

Content-Security-Policy: script-src 'nonce-r4nd' 'strict-dynamic'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; report-to csp

A response header listing what the page may load and execute; a strict nonce policy blocks injected scripts even if XSS gets through.

in practicescript-src 'nonce-…' 'strict-dynamic'; report-only rollouts; violation reports.

deep dive: Disciplines P6

Trusted Types

el.innerHTML = strTypeError(blocked)el.innerHTML = policy .createHTML(str)sanitised, allowed

A CSP feature that makes DOM injection sinks reject plain strings, accepting only values created by a named policy.

in practicerequire-trusted-types-for 'script'; one sanitising policy for the whole app and its dependencies.

deep dive: Disciplines P6

Sanitisation

<img onerror…untrustedDOMPurifyallowlist<img>safe

Removing dangerous elements and attributes from untrusted HTML with an allowlist before inserting it.

in practiceDOMPurify for rich text and markdown output; the Sanitizer API in newer browsers.

deep dive: Disciplines P6

Subresource Integrity

<script src="https://cdn.ex.com/[email protected]" integrity="sha384-oqVuAfXRKap7…" crossorigin="anonymous"></script>

An integrity hash on a script or stylesheet tag so the browser refuses the file if its contents change.

in practiceintegrity="sha384-…" on CDN scripts at versioned URLs.

deep dive: Disciplines P6

Supply-chain attack

maintainer p…malicious ve…postinstallyour CI + bu…

Compromising a dependency (a hijacked maintainer, a typosquat, a malicious install script) so its code runs in your build or users' browsers.

in practiceevent-stream, ua-parser-js, the 2025 npm worm; lockfiles, ignore-scripts and release-age delays.

deep dive: Disciplines P6

Secrets in the bundle

pk_live_… (Stripe)publishable: OK if restrict…sk_live_…secret: never in the bundleFirebase configpublic by design

Anything shipped to the browser is public; keys with real power belong on a server, publishable keys are restricted at the provider.

in practiceVITE_ and NEXT_PUBLIC_ variables; secret scanners on the built bundle.

deep dive: Disciplines P6
THE SECURITY LAYER, IN ORDER
from the browser's trust boundary to the attacks across it and the controls that stop them
swipe the figure sideways, or tap expand for full screen
1/6
boundaries
Boundaries: the origin is the trust unit; the same-origin policy isolates them; sites group origins for cookies.
27

Sessions, identity and isolation

How users are identified and sessions protected, and how the browser isolates sites.

SameSite

Strictsame-site onlyLax (default)+ top-level GET linksNone; Securealways (third-party)

A cookie attribute deciding whether it is sent on cross-site requests: Strict (never), Lax (top-level GET navigations), None (always, needs Secure).

in practiceThe default Lax in modern browsers killing most CSRF; None for embedded third-party widgets.

deep dive: Browser P9

Session token vs JWT

opaque sessionrandom idserver looks uprevoke: delete rowJWTsigned claimsverify signaturerevoke: hard (expiry)

An opaque session id looked up on the server versus a signed self-contained token the server verifies without a lookup.

in practiceJWTs in localStorage stolen by XSS; revocation being hard for JWTs.

deep dive: Trust P1

OAuth 2.0 and OIDC

appauth serverAPIauthorize (PKCE)codecode + verifiertokensBearer access token

OAuth delegates access to an API with tokens; OpenID Connect adds identity on top (an ID token saying who the user is).

in practice"Sign in with Google"; access tokens for APIs, ID tokens for the client's idea of the user.

deep dive: Trust P1

PKCE

verifier (se…challenge = …sent firstverifier sentat token exchange

Proof Key for Code Exchange: the client sends a hash of a secret with the authorisation request and the secret with the token request, so a stolen code is useless.

in practiceRequired for SPAs and mobile apps in OAuth 2.1.

deep dive: Trust P1

Backend for frontend (BFF)

browsercookie onlyBFFholds tokensAPIs

A server owned by the frontend team that holds tokens and secrets, exposing a session cookie and a tailored API to the browser.

in practiceKeeping OAuth tokens out of JS; aggregating microservices for a screen.

Passkeys (WebAuthn)

sitedevicechallengebiometric unlocksigned by private keyverify with public key

Public-key credentials bound to a site and stored on the device or synced, unlocked by biometrics; phishing-resistant by design.

in practicenavigator.credentials.create and get; replacing passwords and SMS OTP.

deep dive: Trust P1

MFA and step-up

logged inwithdraw…riskystep-up:…allowed

Requiring a second factor at login, and a fresh one for risky actions (withdrawals, new payees) even within a session.

in practiceOTP or passkey before changing a payout account; risk-based prompts.

deep dive: Trust P1

postMessage security

window.addEventListener('message', e => { if (e.origin !== 'https://pay.example.com') return; handle(e.data);});frame.postMessage(msg, 'https://pay.example.com');

Cross-window messages must check event.origin and send with an explicit targetOrigin, or any page can talk to yours.

in practicePayment iframes, OAuth popups, embedded widgets.

deep dive: Browser P9

Cross-origin isolation

Cross-Origin-Opener-Policysame-originCross-Origin-Embedder-Pol…require-corp→ crossOriginIsolatedtrue

COOP same-origin plus COEP require-corp: the page gets its own process group and access to SharedArrayBuffer and precise timers.

in practiceWASM threads and high-resolution performance.now(); breaking third-party embeds that do not send CORP.

deep dive: Browser P9

Security headers

Strict-Transport-SecurityHTTPS only, 1 yearX-Content-Type-OptionsnosniffReferrer-Policystrict-origin-when-cross-or…Permissions-Policycamera=(), geolocation=()

HSTS, CSP, frame-ancestors, X-Content-Type-Options, Referrer-Policy and Permissions-Policy: cheap headers that close whole classes of attack.

in practicesecurityheaders.com grades; one header config tested in CI.

deep dive: Disciplines P6

Spectre and site isolation

one process, many sit…side channel readsanother site's memoryprocess per sitenothing to readin the same process

CPU side channels let a script read memory in its own process, so browsers put each site in its own process.

in practiceWhy cross-origin data must not share a renderer; reduced timer precision; CORB and ORB.

deep dive: Browser P9