Iframes and Sandboxing
Date: 2026-08-17
Embedding one document inside another, with a boundary the browser enforces. It’s the strongest isolation available in a page — which is why payment and third-party widgets live in them — and the
sandboxattribute lets you make that isolation stricter than the same-origin policy alone would.
An iframe is an element that embeds a separate document with its own DOM, JavaScript context and origin; sandboxing is the sandbox attribute’s removal of capabilities from that document.
What the boundary gives you
CROSS-ORIGIN IFRAME
parent page iframe (payments.example.com)
├── own DOM │ ├── own DOM ← parent cannot read it
├── own JS context │ ├── own JS context ← cannot be reached
├── own storage │ ├── own storage ← partitioned
└── own CSS │ └── own CSS ← cannot leak either way
│
the ONLY channel: postMessage
The parent cannot read a cross-origin iframe’s contents, and the iframe cannot read the parent’s. That’s The Same-Origin Policy doing its job, and it’s the reason a payment form in an iframe means your page’s JavaScript never touches card details — which is the whole basis of reduced payment-compliance scope.
The sandbox attribute
sandbox removes capabilities. An empty sandbox removes nearly all of them, and each token adds one back.
<!-- maximum restriction: no scripts, no forms, unique opaque origin,
no navigation of the parent, no popups -->
<iframe src="/preview" sandbox></iframe>
<!-- typical for embedded third-party content -->
<iframe src="https://widget.example.com"
sandbox="allow-scripts allow-same-origin allow-forms"
allow="geolocation 'none'; camera 'none'"
referrerpolicy="no-referrer"
loading="lazy"></iframe>| Token | Restores |
|---|---|
allow-scripts | JavaScript execution |
allow-same-origin | Its real origin, so its own storage and cookies work |
allow-forms | Form submission |
allow-popups | window.open |
allow-top-navigation | Navigating the parent page — rarely grant this |
allow-modals | alert, confirm, prompt |
allow-downloads | Initiating downloads |
allow-scripts and allow-same-origin together defeat much of the sandbox for same-origin content — the frame can then reach its own origin’s storage and, if it’s your origin, remove its own sandbox attribute from the parent document. For untrusted content you control, omit allow-same-origin; for a genuinely cross-origin third party, both are usually necessary and the cross-origin boundary is doing the real work.
sandbox and allow are different attributes. sandbox controls document capabilities; allow is the permissions policy controlling access to device APIs — camera, geolocation, payment — Permissions and User Gestures.
Talking across the boundary
postMessage is the only sanctioned channel, and both ends must be defensive.
// parent → iframe. NEVER use '*' as the target origin
iframe.contentWindow.postMessage(
{ type: 'setBasketTotal', pence: 4899 },
'https://payments.example.com' // exact origin. this is a control
);
// iframe → parent, receiving side
window.addEventListener('message', (e) => {
if (e.origin !== 'https://payments.example.com') return; // ← mandatory
if (e.data?.type !== 'paymentComplete') return; // ← validate shape
completeOrder(e.data.reference);
});Two lines carry all the security here. Checking e.origin on receipt — without it, any site that can get a frame or window handle to your page can send you a message that looks legitimate. And naming the exact target origin when sending — '*' broadcasts to whatever document currently occupies that frame, which may not be the one you think if it has navigated.
Treat message data as untrusted input in the same way as a request body. Validate the shape, never pass it to innerHTML or eval — Common Vulnerabilities.
The costs
- They’re expensive. Each iframe is a separate document with its own parsing, styling, layout and often its own JavaScript context. A page with six embeds pays six times — Third-Party Scripts
- Storage is partitioned. An embedded frame no longer shares its own origin’s storage across sites, which breaks embedded single sign-on and returning-customer recognition — Storage Partitioning
- Layout is awkward. An iframe doesn’t size to its content; making it responsive means the child posting its height to the parent, which is a permanent small maintenance cost
- Accessibility needs attention. Give every iframe a
titleattribute — it’s what a screen reader announces — and check that focus moves into and out of it sensibly — Screen Readers, Focus Management - They’re invisible to your analytics. Interactions inside a cross-origin frame produce no events in the parent, so a checkout iframe is a hole in the funnel unless the vendor posts messages out — Checkout Instrumentation Constraints
Clickjacking, the other direction
Everything above is about embedding others. The reverse — someone embedding you — is a real attack: your page is loaded in a transparent iframe over their content, and a click intended for their button lands on your “Confirm order”.
Content-Security-Policy: frame-ancestors 'self' https://partner.example.com
frame-ancestors is the modern control and supersedes the older X-Frame-Options header. Set it on every page that performs an action, which in commerce means basket, checkout, account and admin — Content Security Policy.
Where they’re right
- Payments and card entry, which is the main one and the reason the pattern exists
- Untrusted user content — a preview of user-submitted HTML, sandboxed hard
- Third-party widgets where style collisions would otherwise be certain, though Web Components are often lighter for content you control
- Legacy application embedding during a strangler migration, where two systems must coexist visually
Where it interacts
- The Same-Origin Policy — the boundary iframes rely on, and what it does and doesn’t block
- Content Security Policy —
frame-ancestorsandframe-src, the two directives governing framing in both directions - Storage Partitioning — the change that broke most embedded-identity use cases
- Video and Media — embedded players are the most common iframe on a retail site, and the facade pattern is the mitigation