An <iframe>
or inline frame is one of the most powerful HTML elements and it might look harmless. But that small piece of HTML can open big security holes if you're not careful. In this article, I'll show how an iframe can actually ruin your site, what kind of damage it can do, and how the sandbox
attribute can protect you from it.
What <iframe>
Is Usually Used For
An <iframe>
lets you embed another webpage inside your own. Developers use iframes for many practical reasons such as, embedding YouTube videos or other media players, displaying Google Maps or location embeds, loading Google Tag Manager, or other embedded use cases.
But do you know what an iframe can actually do once it's embedded in your page? The answer depends on how it's configured and whether it's same-origin or cross-origin.
- Same-origin iframes (from the same domain as your page) can access your DOM, cookies, and local storage.
- Cross-origin iframes (from a different domain) are blocked by the browser's Same Origin Policy, so they cannot directly access your cookies, local storage, or DOM. However, they can communicate with the parent page using
postMessage
, which allows them to request actions like navigation or data transfer (the parent must validate messages to stay safe). They can also attempt to open popups or new tabs, though modern browsers usually block these unless triggered by a user gesture.
Insecure iframe Example
To demonstrate this, I built a minimal application that embeds an insecure iframe capable of collecting information about the parent page's visitor. This demo shows how an improperly sandboxed or unaudited iframe can be abused to leak sensitive data.
Parent Page
export default function main() {
return (
<section>
<div className="card">
<h1> Parent Page </h1>
<p> Embedded Page </p>
<iframe src="http://localhost:5173/iframe" />
</div>
</section>
)
}
Embedded Page (iframe)
import { useEffect, useState } from "react";
export default function Iframe() {
const [data, setData] = useState<Record<string, string> | null>(null);
useEffect(() => {
fetch("https://ipapi.co/json/")
.then(r => r.ok ? r.json() : Promise.reject())
.then((d: Record<string, any>) => {
// Function to censor data
const censored: Record<string, string> = {};
Object.keys(d).forEach(key => {
censored[key] = "XXX";
});
setData(censored);
})
.catch(() => setData({ error: "XXX" }));
}, []);
return (
<section>
<h2>Iframe Page</h2>
<p>This iframe fetches IP and location data.</p>
<div>
{data ? (
<pre>{JSON.stringify(data, null, 2)}</pre>
) : "Fetching..."}
</div>
</section>
);
}
Even though this is a same‑origin iframe in the demo, an iframe can fetch cross‑origin resources like any normal browser client. In this example, the embedded iframe calls an external API to retrieve visitor information. The JSON response includes sensitive data such as the user's public IP, city, region, postal code, latitude and longitude and other metadata.

By adding a sandbox
attribute to the iframe, the parent page can prevent or restrict this behavior.
<iframe src="http://localhost:5173/iframe" sandbox="true"/
In the screenshot below, you can see that the data is no longer loaded. The iframe now just shows "Fetching…" instead of the sensitive information.

Sandbox
By default, adding the sandbox
attribute to an <iframe>
imposes strict restrictions on what the embedded page can do. Scripts will not execute, forms cannot be submitted, navigation to the top-level page is blocked, and access to cookies or local storage is denied. These restrictions create a secure environment, preventing the iframe from interacting with the parent page or accessing sensitive visitor information.
If you want to selectively allow certain behaviors, you can add allow-*
keywords such as allow-scripts
to enable JavaScript, allow-same-origin
to permit access to cookies and local storage, or allow-popups
to open new windows or tabs. There are many other options available, which can be combined depending on the specific needs of your application.
So What Does Sandbox Do for Iframes?
To sum it up, the sandbox
attribute helps control what an embedded iframe can do, protecting both the parent page and visitor data. The basic implementation starts with understanding its default behavior and how to selectively grant permissions:
- Apply sandbox by default. A sandboxed iframe blocks scripts, form submissions, top-level navigation, and access to cookies or local storage. This creates a safe default environment where untrusted content cannot interact with your page.
- Grant only the permissions you need. If your iframe requires JavaScript, popups, or same-origin access, use
allow-*
keywords likeallow-scripts
,allow-popups
, orallow-same-origin
. Only grant capabilities that are truly necessary, otherwise you risk exposing sensitive data. - Combine with other security measures. Use
postMessage
validation and Content Security Policy (CSP) alongside sandboxing to ensure that communication and content loading remain safe. - Test and observe behavior. Without permissions, an iframe may refuse to load content or fail silently. With permissions, it can perform the actions you allow, so always check that functionality works as intended without compromising security.
When implemented correctly, sandbox ensures that iframes can serve their purpose without creating vulnerabilities. Start by sandboxing all iframes by default, then carefully enable only what's necessary. This balance of security and functionality keeps both your site and visitors safe.