Iframes. Every time I hear about them it reminds me of the good old days when websites were collections of static pages and internet speeds were measured in kilobits per second. These things called framesets and frames were used to split up a webpage into separate sub-documents—a menu bar frame, a side bar frame, a footer frame—that could each reload on its own so that pages wouldn’t have to be reloaded entirely with every click of a hyperlink. You had the choice of either building your HTML page out of frames or a single body tag—never both. As a compromise, inline frames (iframes) were introduced as a way to embed frames in an HTML document just like any other element, without being forced to use a frameset. These days, with AJAX and WebSockets providing all the interactivity and partial page refresh behavior we could ever need (for now), framesets and frames have long become unnecessary and don’t even exist in HTML5. Yet, iframes are still around, and every indication shows they’re here to stay. In fact, they are the easiest and safest way to embed content from other sites into your page. Of course, as with many web technologies, there is a right way and a wrong way to use iframes, so I’d like to go over how to securely embed other sites’ contents with iframes, and how to prevent others from attacking your site by embedding your content as an iframe.
Everywhere you look, you see embedded YouTube videos, tweets, like buttons, and of course, the Tinfoil Security badge.
Any third-party code that you add directly to your site without the protection of iframes would have all of the same access as your own code. Iframes are the way to go.
If you embed an iframe from your own domain, the browser does not provide any protections against it. Presumably, this is safe since it is your own domain and you likely trust (or wrote) this code. If the iframe comes from a different domain, a browser’s cross-domain policy would kick in, preventing the iframe from accessing cookies, local storage, or the DOM from its embedding document. Even so, cross-domain iframes still have the ability to trigger alerts, run plugins (malicious or otherwise), autoplay videos, and present submittable forms in an attempt to phish users’ information. If all you want the iframe to be able to do is present a social network button, there should be a mechanism to prevent it from doing much else. Thankfully, the ability to restrict iframes is supported by IE 10, Firefox, Chrome, and Safari. It’s called the
Just adding the
sandbox attribute is enough to severely lock down an iframe.
With this attribute set, the document inside the iframe cannot do any of the following:
This is a good starting point for securing an iframe. Then, as you determine what things it needs to perform its normal functions, you can enable just those features. The
sandbox attribute gives us this capability, by allowing you to specify a space-separated list of permissions, with one or more of the following choices.
allow-same-origin- allows the iframe to access cookies and local storage from the parent, as if it came from the same domain.
allow-top-navigation – allows the iframe to navigate the parent to a different URL.
allow-forms – allows form submission
allow-popups – allows the iframe to open new windows or tabs
allow-pointer-lock – allows pointer lock
In the case of the Tinfoil Security badge, the iframe contains a single image that links to a page. This page opens in a new tab or window and shows that your site is verified. The iframe doesn’t need to perform any additional task, so it shouldn’t be allowed to do anything except open pop-ups.
Be careful about giving the iframe too much power. If you sandbox the iframe but give it the permissions
sandbox attribute and reload itself with no restrictions.
By default, an embedded iframe has an
2px inset border around it. If you don’t like it, you may have been tempted to use a new HTML5 iframe attribute called
seamless to get rid of it. (It’s so new, only the latest version of Safari and Chrome support it as of this writing.)
A few top Google search results for “iframes without borders” suggest using this hot new iframe attribute as the way to make iframes look, well, seamless. Unfortunately, this attribute does way, way more than remove borders. A seamless iframe has access to all of the following from the parent document:
The iframe gets access to all of these things even if it is cross-origin. In essence, a seamless iframe acts as if it’s just part of the parent document, and it is granted the same level of trust and power. Sure, the borders are removed, but so is any security the iframe provided in the first place. If all you want is for your iframe to look seamless—without any borders or scrollbars—there is a way to do that without handing over complete control of your website to a third-party. Just use CSS.
<iframe src="http://example.com" allowTransparency="true" scrolling="no" frameborder="0" style="border:none; overflow:hidden;"></iframe>
Only use the seamless attribute if you trust the iframe’s content as if it were your own.
Your site could be at risk even if you don’t include third-party content. If you don’t protect against it, other sites could use you as an iframe. Since the parent site embedding the iframe gets to control the look and feel of the element, they could use a technique called clickjacking to steal your users’ information. Potentially, a malicious site could load your site as a full-page iframe and make it seem like they’re actually visiting your site, say your login page. In front of this iframe they will overlay an invisible form, with a text field in front of both your site’s username and password field. When an unsuspecting user tries to log in to your site, they’ll actually be filling out this malicious form and sending their login credentials to the malicious site.
The best thing you can do to protect yourself is to prevent your site from being embedded as an iframe. These days every browser since IE8 supports the
X-FRAME-OPTIONS response header. If your site sends this header with its value set to
DENY when the page is requested, browsers will refuse to allow the page to be rendered in an iframe. The other supported option is
SAMEORIGIN, which allows the page to be embedded in an iframe only if the parent document is from the same domain, which presumably is also your code.
if (window.top !== window.self) window.top.location.replace(window.self.location.href);
I hope this has convinced you to switch over to using iframes whenever adding untrusted content to your site. If set up properly, they provide the best protective separation between your content and third-party content, especially on new browsers that support sandboxing. Iframes are here to stay. Have any questions or other ideas about embedding third-party content? Sound off in the Hacker News comments.