[KongchangAI]
Tech Frontiers· 4 min read· 2,111 words

CSP Allow-list Experiment: A New Approach to Dynamic Domain Authorization via Sandboxed iframes

CSP Allow-list Experiment: A New Approach to Dynamic Domain Authorization via Sandboxed iframes

Simon Willison's experiment turns CSP allow-listing from silent blocking into interactive user authorization.

Simon Willison released the CSP Allow-list Experiment, a tool that uses a dual-layer architecture of sandboxed iframes, custom fetch interception, and postMessage cross-window communication to transform CSP domain allow-listing from silent blocking into an interactive, user-driven authorization flow. Built rapidly with GPT-5.5, it's ideal for online code editors and sandbox environments, embodying a "Secure by Default, Open by Request" philosophy, though production use would require careful consideration of permission fatigue and social engineering risks.

Overview

Simon Willison recently released an experimental tool called CSP Allow-list Experiment, demonstrating an inspiring web security interaction pattern: application code runs inside a sandboxed iframe protected by CSP (Content Security Policy), and when the application attempts to access an unauthorized domain, the error is automatically intercepted and a prompt appears, letting the user decide whether to add that domain to the allow-list.

This experiment is more than just a technical exercise — it reveals how CSP policies can become more flexible and user-friendly in real-world applications.

What Is CSP? Pain Points of Traditional Allow-list Strategies

CSP Basics

Content Security Policy (CSP) is a browser-provided security mechanism that lets developers declare, via HTTP response headers, which origins a page is allowed to load resources from. For example, the connect-src directive controls which domains the page can connect to via fetch() or XMLHttpRequest, while script-src controls the sources of executable scripts.

The CSP concept was originally proposed by Mozilla around 2004 and was officially published as a W3C Candidate Recommendation (CSP Level 1) in 2012, followed by multiple iterations. CSP Level 2 introduced nonce (one-time random tokens) and hash (script content hashes) mechanisms, allowing developers to precisely authorize specific inline scripts without relying on domain allow-lists. CSP Level 3 further introduced the strict-dynamic directive, which automatically grants execution permissions to sub-scripts dynamically loaded by trusted scripts, greatly simplifying policy configuration for complex applications. Beyond connect-src and script-src, CSP includes over a dozen directives such as img-src (image sources), style-src (stylesheet sources), frame-src (iframe sources), and font-src (font sources), covering virtually every type of resource a page might load.

The core philosophy of CSP is the "allow-list" — only explicitly permitted origins can be accessed, and everything else is denied. This is highly effective in defending against XSS attacks and data exfiltration.

XSS Attacks and CSP's Defense Logic

To understand the value of CSP, you first need to understand the core threat it defends against — Cross-Site Scripting (XSS). The essence of an XSS attack is that an attacker injects malicious scripts into a trusted web page. When other users visit that page, the browser cannot distinguish between legitimate scripts and injected ones, executing all code indiscriminately. Traditional defenses rely primarily on input filtering and output encoding, but these methods are far from foolproof. CSP provides a defense-in-depth approach: even if an attacker successfully injects malicious code, the CSP policy restricts script sources and network connection targets, preventing the injected code from loading additional payloads from the attacker's server or exfiltrating stolen data to unauthorized domains. This "limit the damage even if breached" design philosophy makes CSP an indispensable component of the modern web security stack.

Strict CSP and User Experience Problems

However, strict CSP policies often cause headaches during development and debugging. When a request is blocked by CSP, the browser simply logs a silent error in the console, and the user sees functionality mysteriously failing. Developers must manually check the console, identify blocked domains, modify CSP headers, and redeploy — a workflow that is both tedious and unintuitive.

It's worth noting that the CSP specification provides a report-uri (and the newer report-to) directive, which allows browsers to send violation reports in JSON format to a designated server endpoint. Many large websites (such as GitHub and Twitter) use this mechanism to collect CSP violation data for policy tuning. However, this reporting mechanism is asynchronous and developer-facing — ordinary users cannot perceive it at all, let alone participate in decision-making. Simon's experiment innovates precisely in this gap — transforming violation events from background reports into foreground interactions.

For online code editors or sandbox environments, this problem is even more acute: user-written code may need to access a wide variety of third-party APIs, making it completely impractical to pre-configure allow-lists for every possible domain.

Core Architecture Design

Dual-Layer Architecture: Parent Window + Sandboxed iframe

Simon's experiment employs a parent window + sandboxed iframe dual-layer architecture, with the following overall flow:

  1. Sandboxed iframe isolation: Application code runs inside an iframe protected by strict CSP, with a default policy of default-src 'none' that blocks virtually all external resource loading
  2. Custom fetch() interception layer: Inside the sandbox, the native fetch() is wrapped with a proxy layer that captures errors when requests are blocked by CSP, rather than silently discarding them
  3. postMessage cross-window communication: Captured CSP error information is passed to the parent window via postMessage
  4. User authorization decision: The parent window displays a dialog informing the user: "The sandbox is trying to connect to https://api.inaturalist.org. Would you like to add this domain to the allow-list?"
  5. Dynamic allow-list refresh: After user confirmation, the domain is added to the connect-src allow-list, the iframe is automatically refreshed, and the request goes through normally

iframe Sandbox Isolation Mechanism Explained

The HTML5 <iframe sandbox> attribute is one of the security cornerstones of this architecture. When an iframe has the sandbox attribute, the browser imposes a series of strict restrictions: scripts are disabled by default, form submission is blocked, popups are forbidden, top-level navigation is prevented, and the content is treated as a unique origin (unable to access the parent page's cookies and storage even if on the same domain). Developers can selectively relax restrictions by adding flag values — for example, allow-scripts enables script execution, and allow-same-origin restores same-origin identity. In Simon's experiment, the iframe needs allow-scripts to execute user code, but uses CSP headers rather than sandbox attributes for fine-grained control over network access permissions. This combination of sandbox + CSP creates a double security barrier: the sandbox limits the iframe's capability boundary, while CSP precisely controls the source range of resource loading.

postMessage Cross-Window Communication Security Model

window.postMessage() is an HTML5 cross-origin secure communication API that allows windows of different origins to exchange structured data with built-in security safeguards. The sender can specify the target window's origin (e.g., targetWindow.postMessage(data, 'https://trusted-parent.com')), and the receiver verifies the message source via the event.origin property of the event object. In security-sensitive scenarios, the receiver must strictly validate event.origin; otherwise, malicious pages could forge messages. In Simon's architecture, when the parent window receives CSP violation messages from the sandboxed iframe, it needs to confirm the message genuinely comes from the expected sandbox source, not an injected third-party iframe. This origin-verification-based communication model ensures that the security decision chain itself cannot be tampered with.

Interface Interaction Details

From the tool's screenshots, the interface is split into two panels: the left side is an HTML source code editor, and the right side is a live preview area, with the currently active CSP headers displayed at the top. When code in the sandbox attempts to request api.inaturalist.org, a modal dialog pops up clearly showing the intercepted domain and offering "Cancel" and "OK" options.

At the bottom, there's an "Allowed fetch() origins" management area where users can manually view, add, and remove authorized domains from the list.

Technical Highlights and Practical Use Cases

From Silent Interception to Interactive Security Decisions

The most valuable aspect of this experiment is that it transforms CSP from a silent background interception mechanism into a user-perceivable, user-participatory security decision process. This design approach is particularly well-suited for:

  • Online code editors and sandbox environments: In tools like CodePen and JSFiddle, user-written code may need to call various external APIs — on-demand authorization is far more practical than pre-configured allow-lists
  • Plugin and extension systems: Third-party code runs in a restricted environment and requests network access permissions on demand, similar to mobile app permission prompts
  • Web security educational tools: Helping learners intuitively understand how CSP works and its interception mechanisms

Comparison with Mobile Runtime Permission Models

This "on-demand authorization" interaction pattern shares a striking resemblance to the runtime permission model introduced in Android 6.0 (2015) and iOS's privacy permission prompts. Before the mobile runtime permission model existed, Android apps declared all required permissions at install time, and users could only accept everything or abandon the installation — very much like the traditional CSP approach of "one-time configuration at deployment." The core idea of the runtime permission model is to defer permission decisions to the moment they're actually needed, letting users make informed decisions within specific usage contexts. Research shows this model significantly improves users' understanding and deliberation when granting permissions. Simon's experiment brings the same concept to the web security policy realm: rather than predicting all possible network requests at deployment time, it lets users decide whether to trust a target domain when the request actually occurs, within concrete context.

Rapid Prototyping with GPT-5.5

Simon mentioned that this tool was built using GPT-5.5 xhigh in the Codex desktop application. The fact that an experimental tool involving CSP configuration, iframe sandbox isolation, postMessage cross-origin communication, and multiple other web security concepts was prototyped in a short time with AI-assisted programming speaks to the practical value of AI for rapidly validating technical ideas.

OpenAI's Codex desktop application (also known as the GUI version of Codex CLI) is a developer-facing AI programming assistant capable of understanding project context and executing multi-step code generation and modification tasks. Unlike simple code completion, Codex can autonomously plan implementation paths and generate complete functional modules based on high-level natural language descriptions. For projects like this CSP sandbox that involve interactions across multiple browser APIs, the advantages of AI-assisted programming are especially pronounced: developers don't need to individually look up CSP directive syntax, sandbox attribute flags, postMessage security constraints, and other details scattered across different specification documents — the AI can integrate this knowledge and directly generate a runnable code skeleton, letting developers focus on architecture design and security logic validation. This "idea to prototype" acceleration capability is particularly valuable for proof-of-concept work in security research — researchers can quickly build experiments to validate or refute a security hypothesis without spending excessive time on engineering implementation details.

Security Considerations and Limitations

It must be emphasized that this is still an experimental project. If a similar dynamic CSP allow-list mechanism were to be adopted in production environments, several issues need serious consideration:

  • User cognitive burden: Ordinary users may not understand what security risks are implied by authorizing a given domain. There's a classic concept in security research called "Permission Fatigue," referring to how users gradually lose vigilance when frequently confronted with permission requests and tend to click "Allow" without thinking. Android and iOS permission prompts have already exhibited this phenomenon, and the web environment should be equally cautious
  • Social engineering attack risks: Malicious code could craft carefully designed prompt text to trick users into authorizing dangerous domains. For example, an attacker might disguise a data exfiltration domain with an innocuous-sounding name (like analytics-cdn.com or font-service.net), exploiting users' trust in common service names to bypass scrutiny
  • Allow-list persistence management: Storage, synchronization, and expiration cleanup of domain lists all require robust mechanisms. In practice, you'd also need to consider the scope of the allow-list (global vs. session-only), whether wildcard matching is supported (e.g., *.github.io), and revocation mechanisms for domains that have been maliciously taken over
  • CSP bypass risks: Dynamically modifying security policies inherently requires extra caution to avoid introducing new attack surfaces. Notably, CSP headers can only be set at HTTP response time and cannot be dynamically modified via client-side JavaScript (CSP set via <meta> tags also only takes effect at page load time). Therefore, in Simon's experiment, modifying the allow-list requires reloading the iframe, which essentially regenerates a response with updated CSP headers — this design is inherently safe because it adheres to the fundamental principle that CSP cannot be tampered with by the client

Nevertheless, as an exploratory direction, this experiment provides valuable reference ideas for improving the CSP user experience.

Conclusion

CSP Allow-list Experiment is an elegant proof-of-concept project that demonstrates how to transform CSP domain authorization into an interactive process through iframe sandbox isolation, custom fetch interception, and parent-child window postMessage communication. While it's still a considerable distance from production-ready applications, it opens up new imaginative space for user experience design in web security policies.

From a broader perspective, this experiment touches on a long-standing tension in web security: the balance between security and usability. Overly strict security policies impede normal functionality, while overly permissive ones are effectively useless. Simon's experiment proposes a middle path — through interactive authorization, security decisions become transparent and controllable while maintaining a strict default posture. This approach aligns closely with the modern security design principle of "Secure by Default, Open by Request."

If you're building an online code editor, sandbox runtime environment, or security educational tool, the architectural ideas in this experiment are well worth studying and drawing inspiration from.

#CSP allow-list#Content Security Policy#sandboxed iframe#postMessage#web security#dynamic authorization#Simon Willison#fetch interception#runtime permissions#XSS defense
Share:

Related articles