← Recipes

阅读中文版本

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.

~25 mincspiframesecurity

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.example

Expected 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.