iframe sandbox: it narrows capability, it does not harden
The sandbox attribute starts by removing everything and allow-* adds pieces back. Getting the direction backwards is common: scripts stop running because that is the default.
The semantics of <iframe sandbox> are easy to flip: the empty value is the strictest. It removes scripts, forms, same-origin status, popups, downloads and more, then allow-* adds individual pieces back.
<iframe sandbox="" src="/embed/comment.html"></iframe>
Inside that iframe JavaScript does not run, forms do not submit, and the content is treated as a separate origin. Fine for passive display.
Granting capability back
| Value | Restores | Risk |
|---|---|---|
allow-scripts |
JavaScript execution | dangerous only with the next row |
allow-same-origin |
original origin, cookie access | with scripts, the sandbox is gone |
allow-forms |
form submission | medium |
allow-popups |
opening windows | medium, phishable |
allow-top-navigation |
navigating the top page | high, can hijack the page |
The dangerous pair
sandbox="allow-scripts allow-same-origin" is effectively no sandbox when the framed page shares the parent’s origin. The script runs and can reach the parent DOM, which means it can delete the sandbox attribute and reload itself.
The fix is to host embedded content on a different origin (a separate subdomain). Then allow-same-origin means “the subdomain’s own origin” and cross-origin isolation still holds.
Do not use it as CSP
sandbox governs what this iframe may do; CSP governs what this document may load. They complement rather than replace each other:
- Block inline scripts → CSP
script-src - Stop an iframe navigating the parent → sandbox
- Stop an iframe making requests → only the iframe’s own CSP
Practical settings
| Embedded thing | Suggested |
|---|---|
| Third-party comments | allow-scripts allow-same-origin allow-popups |
| Video player | allow-scripts allow-same-origin allow-presentation |
| Static document | `` (empty) |
| User-submitted HTML | not an iframe: sanitize, separate origin |
That last row is the floor: never load user-submitted HTML in an iframe. Same-origin sandbox has a history of bypasses; a separate origin is the reliable boundary.
sandbox is an allowlist, not a denylist. Before writing each
allow-*, ask what happens if the capability stays removed.

Comments
…