Sandbox an Iframe with CSP and Sandbox Attributes
Load untrusted content in an iframe with a strict sandbox attribute and a matching frame-src CSP rule so the framed page cannot script its parent or navigate the top window.
Task
Embed a third-party widget in an iframe that cannot access cookies, run plugins, or navigate the top window even if it is compromised.
When to use this
A marketing widget needs to live on the checkout page, but it is third-party code. The team wants the widget to render without the ability to read the session cookie or redirect the user.
Prerequisites
- A page that already sends a Content-Security-Policy header
- The origin of the third-party widget
- A test that the widget still renders after the sandbox is applied
Steps
1. Add the iframe with a sandbox attribute that allows only scripts and same-origin, explicitly omitting allow-top-navigation.
<iframe src="https://widget.example/embed"
sandbox="allow-scripts allow-same-origin allow-popups"
loading="lazy"></iframe>
Expected output: The widget renders and runs its own scripts, but any attempt to set top.location throws a SecurityError.
2. Tighten the CSP frame-src directive to the widget origin so no other iframe source is allowed.
Content-Security-Policy: frame-src https://widget.example;
Expected output: An iframe pointing at a different origin is blocked by the browser and reported in the CSP report endpoint.
3. Add a reporting endpoint so violations are visible without breaking the page in production.
curl -H "Content-Security-Policy-Report-Only: frame-src https://widget.example; report-uri /csp-report" https://checkout.exampleExpected output: The report-only header logs violations to the endpoint without blocking them, so you can tighten safely.
Troubleshooting
Problem
The widget needs to communicate with the parent
Solution
Use postMessage with an origin check on both sides; the sandbox still blocks navigation while allowing message passing.
Problem
allow-same-origin lets the iframe read its own cookies
Solution
If the widget does not need cookies, drop allow-same-origin entirely; the combination allow-scripts allow-same-origin is the most permissive you should ever ship.
Limitations
- A sandboxed iframe with allow-scripts allow-same-origin can still run arbitrary JavaScript; it is sandboxed, not safe.
- CSP frame-src does not constrain the iframe content once loaded; the sandbox attribute does that.
Two layers of sandbox
The sandbox attribute constrains what the framed page can do; the CSP frame-src constrains what you can frame in the first place. Use both, so a compromised widget cannot escalate and a rogue iframe cannot load.
<iframe src="https://widget.example/embed"
sandbox="allow-scripts allow-same-origin allow-popups"
loading="lazy"></iframe>
Report before you block
Ship the CSP as report-only first and watch the reports. Once the reports are clean, switch to enforcing. This avoids breaking a widget that quietly framed a helper from a different origin.
Content-Security-Policy-Report-Only: frame-src https://widget.example; report-uri /csp-report
Expected outcome
The widget renders and runs its own scripts, top.location throws, and an iframe pointing at a different origin is blocked. The report endpoint shows zero violations after the enforcing header ships.