Technical Insight

X-Frame-Options (XFO) Detection from Javascript

X-Frame-Options (XFO) is an HTTP response header, mostly used to combat Clickjacking, that informs a Web browser if the page should be rendered in a <* frame> or <* iframe>. “X-Frame-Options: deny” means that the browser should never allow the page to be framed. “X-Frame-Options: sameorigin” means only the hosting domain is allowed to frame the page.  In either case, a third-party website is never allowed to frame the page. No frames, no Clickjacking.

There are certain circumstances where it is useful for an attacker to know if an iFrame is being blocked by XFO from within Javascript space. For example, I was recently improving upon some Javascript cross-domain login detection code. I noticed a particular URL had a interesting boolean state. When a user is logged-in, no XFO header is sent. When the use was NOT logged-in, a login screen would appear, and of course an XFO header was there to protect it. So, if I could tell XFOs existence, I’d have yet another technique to perform cross-domain login checks.

I noticed that iFrames do not fire OnLoad event handlers, and why would they? We can use this behavior to test for the existence of XFO headers. To do so we create an iFrame, set the OnLoad functionality to immediately remove the iFrame from the DOM, and then use a setTimeout to check for its existence a few seconds later. If the iFrame still exists, this likely means an XFO was present which prevented the iFrame removal (via the OnLoad). Simple.

Proof-of-Concept code is below and RSnake was kind enough to host a live demo. You’ll have to suffer through my non-standard Javascript, I’ve recently been learning Dojo Toolkit. Enjoy!

<* script src=”http://ajax.googleapis.com/ajax/libs/dojo/1.7.2/dojo/dojo.js”><* /script>

<* script>

var urls = [

‘http://www.wikipedia.org/’,

‘http://ha.ckers.org/’,

‘http://www.google.com/’,

‘http://www.facebook.com/’,

‘https://github.com/’,

‘http://daringfireball.net/’,

];

function detect() {

dojo.forEach(urls, function(url) {

var iframe = dojo.create(“iframe”, { src: url, id: url });

dojo.attr(iframe, “style”, {display: ‘none’});

dojo.connect(iframe, “onload”, function() {

dojo.destroy(iframe);

});

dojo.place(iframe, dojo.body());

setTimeout(function () {

var obj = dojo.byId(url);

if (obj) {

dojo.destroy(iframe);

var entry = dojo.create(“li”, null, dojo.body());

entry.innerHTML = “Yes: ” + url;

} else {

var entry = dojo.create(“li”, null, dojo.body());

entry.innerHTML = “No: ” + url;

}

}, 3000);

});

}

<* /script>