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.

← Back to all posts

Comments

…