Tags: web-dev concept

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 sandbox attribute 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>
TokenRestores
allow-scriptsJavaScript execution
allow-same-originIts real origin, so its own storage and cookies work
allow-formsForm submission
allow-popupswindow.open
allow-top-navigationNavigating the parent page — rarely grant this
allow-modalsalert, confirm, prompt
allow-downloadsInitiating 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 title attribute — 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-ancestors and frame-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