Zero Shot Labs — Web Architecture

The web
locked
itself in.

Every security standard added in the last 20 years was designed to protect the publisher from the user — and the user from the publisher. Nobody asked whether the user should have any power at all.

scroll to explore
01 — A brief history

From open to hostile in 25 years

The early web had no security model because it had no threat model. Pages were static, servers were simple, and "the web" meant hyperlinked documents — not applications. What followed was a reactive sequence: each exploit forced a new lock, each lock reduced flexibility, and none of them were designed with user agency in mind.

1993 – 1999
Zero gatekeeping

Any page could load any resource from any server and read its contents. Cookies were introduced but sent cross-site — nobody thought to block them. The web was genuinely permissionless at the application layer. Flash accessed camera and microphone silently, with no browser prompt at all.

1999 – 2006
XHR and the anything-goes era

XMLHttpRequest shipped with no cross-origin block — pages freely fetched third-party APIs and read full responses. ActiveX had full system access in IE. Browser plugins ran with OS-level privileges. Powerful, dangerous, and entirely publisher-controlled.

2006 – 2010
The first locks — CSRF forces Same-Origin

Cross-site request forgery attacks forced browsers to block cross-origin response reading. The Same-Origin Policy hardened. Geolocation arrived in Firefox 3.5 with an inconsistent notification bar — easily dismissed, no persistent state, no way to query what had been granted.

2010 – 2015
Ad-hoc chaos — every API invents its own model

WebRTC introduced camera/mic prompts — per-call, per-session, no memory. Notifications had its own grant model. Geolocation had another. Each spec team built their own UI, persistence, and revocation. No unified way to query what a site had been granted.

2015 – present
Permissions API — unified UI, unchanged power structure

navigator.permissions.query() finally lets pages ask "what have I been granted?" without triggering a prompt. A meaningful improvement. But the model didn't move: the site requests, the browser intermediates, the user reacts. The user cannot proactively grant, scope, or delegate a permission.

"Flash, ironically, was closer to a user-first model in one narrow sense — plugins were user-installed, user-trusted software. Browser vendors killed that model for good security reasons. But they never replaced the user agency part of it."

02 — The security wall

Six layers. Zero user controls.

A request from your application to the open web travels through six distinct enforcement layers. Each was added reactively after a specific attack. None were designed with end-user agency in mind. Click any layer to expand it — then click a mechanism for its full story.

app
your application / user
request originates here
↓  passes through every layer below  ↓
L6
browser / user agent
Permissions API Storage partitioning 3rd-party cookie block
The outermost browser-enforced layer — closest to the user, yet entirely controlled by the browser vendor. All three mechanisms here were added reactively to plug specific tracking and data-leak exploits. The user cannot configure, scope, or override any of them.
Permissions API
Introduced 2015 · W3C spec
Provides a unified interface to query the state of browser permissions (camera, mic, notifications, geolocation) without triggering a prompt. Before this, developers had to attempt the action and catch errors to discover the grant state.
why it hurtsThe user can only react to publisher-initiated prompts. There is no interface for users to proactively grant scoped or time-limited permissions, delegate them, or attach conditions. The prompt is the entire user interface — take it or leave it.
Storage partitioning
Rolled out 2020–2023 · all major browsers
Siloes IndexedDB, Cache API, localStorage, and cookies by top-level site. An embedded widget on site A cannot share storage state with the same widget on site B, even if they share an origin.
why it hurtsKills legitimate cross-site state like "user is logged in to this auth provider" — so every site must implement its own auth, driving centralization toward identity giants (Google, Apple, Meta) who are the only ones with sufficient surface area to stay unaffected.
3rd-party cookie block
Safari 2017 · Firefox 2019 · Chrome 2024
Prevents embedded resources from reading or writing cookies associated with their origin when loaded on a different site. Intended to stop cross-site tracking by ad networks.
why it hurtsAlso breaks federated login, payment widgets, embedded chat, cross-domain analytics, and any legitimate use case that relied on shared session state. The cure — Privacy Sandbox APIs — remains under browser vendor control, trading one monopoly for another.
L5
javascript runtime
Same-Origin Policy Trusted Types Subresource Integrity
The JS runtime sandbox is where the original sin of the web lives: cross-origin access was never restricted until attackers demonstrated it had to be. Each mechanism here was bolted on after a class of attack became widespread.
Same-Origin Policy
Codified ~2006 · oldest enforced browser boundary
Scripts from origin A cannot read the DOM or fetch responses from origin B (differing in scheme, host, or port). The browser withholds the response — but the server still receives and executes the request regardless.
why it hurtsThe most fundamental wall on the web. Everything since — CORS, COEP, COOP — is an attempt to carve controlled exceptions back into this absolute prohibition. The entire developer surface area of web security is patching and re-holing SOP.
Trusted Types
Chrome 83 (2020) · experimental elsewhere
Requires strings passed to dangerous DOM sinks (innerHTML, eval, setTimeout-with-string) to first pass through a registered policy that returns a typed object. Untyped strings are rejected at runtime.
why it hurtsEvery third-party library that writes to innerHTML — which is most of them — breaks on a Trusted Types page. The compliance burden falls entirely on app developers, not on the library authors who created the risky patterns.
Subresource Integrity
Introduced 2016 · all browsers
The integrity attribute on <script> and <link> tags declares a SHA hash. If the fetched resource doesn't match, the browser refuses to execute it — blocking supply-chain attacks via CDN compromise.
why it hurtsRequires developers to compute and hard-code hash values for every external asset. Any CDN-side update — even a legitimate patch — instantly breaks pages until hashes are recomputed and redeployed. Fundamentally incompatible with dynamically-versioned CDN assets.
L4
browser isolation — CO*P headers
COEP COOP CORP SameSite cookies
The CO*P family emerged entirely from the Spectre CPU vulnerability (2018). Spectre let JavaScript read arbitrary process memory via timing side-channels — the only real fix was process isolation, enforced through these headers. Every mechanism here exists because of one exploit discovered in CPU microarchitecture.
COEP
Cross-Origin Embedder Policy · 2020
When set to require-corp, prevents a page from loading any cross-origin resource unless that resource explicitly opts in with a CORP header or CORS response. Required to enable SharedArrayBuffer and high-resolution timers.
why it hurtsEnabling COEP breaks most third-party embeds immediately — analytics, ads, widgets, fonts, CDN assets — because the vast majority of the web hasn't added CORP headers. You cannot ship COEP without auditing and negotiating with every third-party vendor you depend on.
COOP
Cross-Origin Opener Policy · 2020
Severs the relationship between your page and any window that opened it (or that it opened) from a different origin. Prevents cross-origin windows from sharing a browsing context group — isolating your process from Spectre side-channels.
why it hurtsOAuth and payment flows depend on window.opener — the popup talks back to the parent. COOP breaks this entirely. Teams building login or checkout flows must redesign around postMessage-based protocols or full redirect flows, adding weeks per integration.
CORP
Cross-Origin Resource Policy · 2018
A response header telling the browser to refuse any cross-origin read of this resource — even with a valid CORS request. Stronger than CORS: no exceptions, no credential modes, no wildcard escape hatches.
why it hurtsEvery asset server (CDN, image host, API) must now explicitly declare its sharing policy. Anything that doesn't add CORP becomes invisible to COEP-enabled pages — a configuration tax on the entire web's infrastructure, retroactively.
SameSite cookies
Proposed 2016 · Chrome default 2020
The SameSite attribute controls when cookies are sent on cross-site requests. Strict blocks cookies on all cross-site requests including top-level navigations. Chrome made Lax the default in 2020.
why it hurtsStrict blocks cookies even when a user clicks a link from email to your site — they arrive logged out. Lax breaks embedded forms, POST requests from external sites, and multi-domain architectures that assumed cookies flowed freely across navigations.
L3
http response headers
CORS CSP X-Frame-Options Referrer-Policy
The header layer is where publisher control is most explicit. Every mechanism here is a server-side declaration — the publisher decides what is permitted. The user has zero input. Misconfiguring any of these silently blocks legitimate requests with no useful error message to end users.
CORS
Cross-Origin Resource Sharing · W3C 2014
The server declares which origins may read its responses via Access-Control-Allow-Origin. The browser always sends the request and the server always processes it — but withholds the response from JavaScript if the header is absent or mismatched.
why it hurtsCORS does not block the request. A DELETE from a malicious origin executes on the server — CORS only stops JS from reading the response. This surprises teams who treat CORS as a security boundary. It is a readability boundary. Server-side auth is still the only real protection.
CSP
Content Security Policy · 2012 → ongoing
An allowlist in a response header declaring which origins may provide scripts, styles, images, fonts, and connections. Any resource from an unlisted origin is blocked. Designed to prevent XSS by making inline scripts and eval illegal without explicit declaration.
why it hurtsMost production CSP headers contain 'unsafe-inline' — which disables XSS protection entirely. The overhead of maintaining an accurate allowlist across every third-party dependency, CDN version bump, and A/B test framework makes strict CSP operationally prohibitive for most teams.
X-Frame-Options
De facto standard ~2009 · superseded by CSP frame-ancestors
Prevents a page from loading inside an <iframe> on a different origin. DENY blocks all framing. SAMEORIGIN permits only same-origin frames. ALLOW-FROM was deprecated and ignored by most browsers.
why it hurtsThe lack of a working multi-origin ALLOW-FROM means publishers wanting to permit embedding on specific partners must use CSP frame-ancestors — a newer directive still not universally supported. Every Stripe checkout or support widget embed requires bilateral coordination.
Referrer-Policy
Standardized 2017 · all browsers
Controls how much of the originating URL appears in the Referer header when navigating or loading subresources. Options range from no-referrer to unsafe-url. Browsers changed their default to strict-origin-when-cross-origin in 2021.
why it hurtsAnalytics pipelines, affiliate tracking, and A/B testing attribution all depended on referrer data. The new browser default broke path-level attribution — teams now pepper URLs with UTM parameters everywhere as a workaround, trading clean URLs for tracking hygiene.
L2
transport / TLS
HSTS Mixed content block Certificate pinning
The transport layer ensures the channel is encrypted and authenticated. All three mechanisms here were responses to active downgrade attacks — cases where attackers intercepted connections to strip encryption or swap certificates. Each trades developer or operator flexibility for connection integrity.
HSTS
HTTP Strict Transport Security · RFC 6797 (2012)
A response header instructing the browser to refuse plain HTTP for this host for a declared max-age — up to 2 years. Subsequent HTTP requests are upgraded to HTTPS before leaving the browser, preventing SSL stripping attacks.
why it hurtsOnce set with a long max-age, it cannot be easily reversed. If a cert expires or a host goes HTTP-only, users are locked out for the full max-age period. The HSTS preload list baked into browsers is even more permanent — removal takes months and a full browser release cycle.
Mixed content block
Progressive enforcement 2015–2020
An HTTPS page may not load subresources over plain HTTP. Browsers first warned, then blocked passively-loaded mixed content, then blocked all mixed content. Chrome auto-upgrades or blocks HTTP subresources on HTTPS pages.
why it hurtsMigrating large sites to HTTPS surfaces hundreds of hardcoded http:// references in content, embeds, and legacy assets — including third-party widgets that haven't migrated. Pages break silently with no useful error message to end users.
Certificate pinning
HPKP deprecated 2018 · app-level pinning ongoing
Associates a specific public key or CA with a host. Connections using any other certificate are rejected — preventing MITM attacks using fraudulently-issued certs. HPKP was a browser header; app-level pinning remains common in native apps.
why it hurtsHPKP was deprecated because misconfiguration permanently locked users out of sites — a wrong pin made the entire domain unreachable for the pin's lifetime. App-level pinning breaks corporate proxies, developer debugging tools, and any cert rotation. The browser standard was abandoned entirely.
L1
dns / network
DNSSEC DNS-over-HTTPS
The lowest layer — before a TCP connection is ever opened. DNS is a 1983 protocol with no authentication and no encryption, running on global infrastructure of recursive resolvers. Both mechanisms address the same fundamental problem: DNS responses can be forged or observed. Neither is universally deployed.
DNSSEC
RFCs 4033–4035 (2005) · deployment still incomplete
Adds cryptographic signatures to DNS records. Resolvers can verify that a response was signed by the zone's authoritative server and hasn't been tampered with in transit — preventing cache poisoning attacks.
why it hurts20 years after standardization, DNSSEC deployment is still below 30% of top-level domains. Key rotation is manual and error-prone, and a misconfigured DNSSEC record makes the entire domain unreachable — not degraded, fully unreachable. The operational risk discourages adoption.
DNS-over-HTTPS
RFC 8484 (2018) · Chrome/Firefox default in some regions
Tunnels DNS queries inside encrypted HTTPS connections, hiding lookups from network observers. Queries go to a configured DoH resolver — often operated by the browser vendor — rather than the system's DNS server.
why it hurtsISPs and network operators lose visibility into DNS traffic — used for content filtering, parental controls, and corporate security monitoring. Enterprises must deploy their own DoH servers or override browser policy. Centralizes DNS resolution toward browser vendors and a handful of large DoH operators.
web
the open web — apis, resources, third-party content
your destination, if every layer permits it

Notice that zero of these layers give the user any affirmative controls. Each is either a server-side declaration (publisher decides) or browser-side enforcement (vendor decides). The user is entirely reactive.

03 — Content Security Policy

The allowlist that never ends

CSP's premise is elegant: tell the browser which origins are trusted for scripts, styles, images, and connections — block everything else. The implementation is where friction compounds. Every third-party dependency, CDN, analytics tool, font provider, or A/B test framework requires a server-side header change and a redeploy.

"Most real-world CSP headers in production contain 'unsafe-inline' — which disables XSS protection entirely. The allowlist remains, adding developer friction, while providing no security."

Toggle sources on and off. Pay attention to what happens when you enable 'unsafe-inline'.

Content-Security-Policy: default-src 'none'
0
allowed
0
blocked
0%
pass rate
Toggle sources above to see how the policy affects each resource.

The nonce escape hatch

Modern CSP offers nonce-{random} as an alternative to 'unsafe-inline' — each inline script gets a unique token generated server-side per request. This works, but now every HTML response needs a fresh nonce, CDN caching is invalidated, and third-party snippets that inject their own scripts break anyway because they don't receive the nonce.

04 — The pattern

Reactive locks, never user tools

Zoom out across all 16 mechanisms and a single pattern emerges. Every one was added in response to a specific attack — XSS, CSRF, Spectre, clickjacking, DNS poisoning. None were designed by asking "what should the user be able to do?" They are publisher protections and vendor constraints dressed up as user safety.

How each lock got added

  • An exploit is published or deployed at scale
  • Browser vendors patch reactively under pressure
  • A new spec emerges from W3C or WHATWG
  • Adoption is enforced via deprecation warnings
  • Developers update headers or break their sites

What never gets asked

  • Can the user override this for trusted sites?
  • Can the user proxy or delegate this permission?
  • Can the user set their own trust policies?
  • Can the user see what data left their browser?
  • Can the user undo any of this?

Where power actually sits

  • Publishers control CORS, CSP, cookie policy
  • Browser vendors control enforcement behavior
  • Ad networks manipulate spec priority via lobbying
  • Users can only accept or not visit the site

The compliance trap for builders

  • Hundreds of startups tried to build web agents
  • Each ran into the same specification wall
  • The cost of compliance exceeds most funding rounds
  • The standards move faster than implementation

"The web's security model was never designed. It accumulated — one lock at a time — until navigating it required an enterprise team just to ship a button."

05 — What comes next

A user-first internet layer

The security mechanisms documented here aren't going away — nor should most of them. The problem isn't that the web has security. The problem is that security has been the only frame. No equivalent investment has been made in user capability, user memory, or user agency. That's the gap.

What if the user had their own layer — running above the publisher stack, not inside it — that could hold their identity, their context, their trust decisions, and their data?
01
User-held identity

Instead of each publisher storing a user record, the user holds a portable identity that publishers can request access to — not the other way around.

02
Persistent context across sessions

The web has no memory. The user's layer does — a semantic graph of what they've read, decided, and built, accessible to any tool they trust.

03
Proactive permission delegation

Instead of reacting to publisher prompts, users set their own policies: "allow api.acme.com camera access for 30 minutes, proxied through my layer."

04
Tokenized value exchange

When a user's data or attention generates value, the ledger is visible to them — not hidden inside an ad server. They choose to participate or opt out.