Is there a client-side way to detect X-Frame-Options?
Asked Answered
E

8

49

Is there any good way to detect when a page isn't going to display in a frame because of the X-Frame-Options header? I know I can request the page serverside and look for the header, but I was curious if the browser has any mechanism for catching this error.

Emplane answered 31/10, 2011 at 5:7 Comment(3)
As fas as I know and after some research, it's currently not possible to find it out from client side. I've tried loading an iFrame src using a "protected" URL, wrapping it with a try/catch but it didn't work. If you happen to find a solution, please share it. Thank you.Gogh
I started a Webkit bug for this. bugs.webkit.org/show_bug.cgi?id=90660Emplane
All of the answers below are wrong! You can't use XHR request and reading the headers due to Same-origin policy. You can't use frame.contentWindow or frame.contentDocument, in many cases they are null. I don't know what is the solution, but I tested all of the answers below and non of them work.Poree
T
13

OK, this one is old but still relevant.

Fact: When an iframe loads a url which is blocked by a X-Frame-Options the loading time is very short.

Hack: So if the onload occurs immediately I know it's probably a X-Frame-Options issue.

Disclaimer: This is probably one of the 'hackiest' code I've written, so don't expect much:

var timepast=false; 
var iframe = document.createElement("iframe");

iframe.style.cssText = "position:fixed; top:0px; left:0px; bottom:0px; right:0px; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;";
iframe.src = "http://pix.do"; // This will work
//iframe.src = "http://google.com"; // This won't work
iframe.id = "theFrame";

// If more then 500ms past that means a page is loading inside the iFrame
setTimeout(function() {
    timepast = true;
},500);

if (iframe.attachEvent){
    iframe.attachEvent("onload", function(){
    if(timepast) {
            console.log("It's PROBABLY OK");
        }
        else {
            console.log("It's PROBABLY NOT OK");
        }
    });
} 
else {
    iframe.onload = function(){
        if(timepast) {
            console.log("It's PROBABLY OK");
        }
        else {
            console.log("It's PROBABLY NOT OK");
        }
    };
}
document.body.appendChild(iframe);
Titmouse answered 18/2, 2014 at 11:17 Comment(2)
Very unreliable if the client's connection has bad latency to begin with.Nga
Bunch of things to mention here. Yes, it's unreliable because of the speed (ie the 500msec, could that be 1000msec or maybe 2 million msec?) It depends on the connection speed. For that purpose, you might want to do some tricks: preload the DNS, for example. Or hit a separate page, track how long it takes to load, and based on that time, set your 500msec to 1 hour or 100msec. It depends. The next thing, is that if the page does NOT load, you still don't know if it was because of the X-Frame-Options header. You just know it did not load, which might be all you need to know.Fusspot
W
7

Disclaimer: this answer I wrote in 2012(Chrome was version ~20 at that time) is outdated and I'll keep it here for historical purposes only. Read and use at your own risk.


Ok, this is a bit old question, but here's what I found out (it's not a complete answer) for Chrome/Chromium.

the way do detect if a frame pointing to a foreign address has loaded is simply to try to access its contentWindow or document.

here's the code I used:

element.innerHTML = '<iframe class="innerPopupIframe" width="100%" height="100%" src="'+href+'"></iframe>';
myframe = $(element).find('iframe');

then, later:

try {
    var letstrythis = myframe.contentWindow;
} catch(ex) {
    alert('the frame has surely started loading');
}

the fact is, if the X-Frame-Options forbid access, then myFrame.contentWindow will be accessible.

the problem here is what I called "then, later". I haven't figured out yet on what to rely, which event to subsribe to find when is the good time to perform the test.

Windpipe answered 20/4, 2012 at 15:13 Comment(1)
This no longer works. Accessing contentWindow throws no exception.Quadriga
N
2

This is based on @Iftach's answer, but is a slightly less hacky.

It checks to see if iframe.contentWindow.length > 0 which would suggest that the iframe has successfully loaded.

Additionally, it checks to see if the iframe onload event has fired within 5s and alerts that too. This catches failed loading of mixed content (in an albeit hacky manner).

var iframeLoaded = false;
var iframe = document.createElement('iframe');

// ***** SWAP THE `iframe.src` VALUE BELOW FOR DIFFERENT RESULTS ***** //
// iframe.src = "https://davidsimpson.me"; // This will work
iframe.src = "https://google.com"; // This won't work

iframe.id = 'theFrame';
iframe.style.cssText = 'position:fixed; top:40px; left:10px; bottom:10px;'
 + 'right:10px; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;';

var iframeOnloadEvent = function () {
    iframeLoaded = true;
  var consoleDiv = document.getElementById('console');
    if (iframe.contentWindow.length > 0) {
    consoleDiv.innerHTML = '✔ Content window loaded: ' + iframe.src;
    consoleDiv.style.cssText = 'color: green;'
    } else {
    consoleDiv.innerHTML = '✘ Content window failed to load: ' + iframe.src;
    consoleDiv.style.cssText = 'color: red;'
    }
} 

if (iframe.attachEvent){
    iframe.attachEvent('onload', iframeOnloadEvent);
} else {
    iframe.onload = iframeOnloadEvent;
}
document.body.appendChild(iframe);

// iframe.onload event doesn't trigger in firefox if loading mixed content (http iframe in https parent) and it is blocked.
setTimeout(function () {
    if (iframeLoaded === false) {
        console.error('%c✘ iframe failed to load within 5s', 'font-size: 2em;');
    consoleDiv.innerHTML = '✘ iframe failed to load within 5s: ' + iframe.src;
    consoleDiv.style.cssText = 'color: red;'    
  }
}, 5000);

Live demo here - https://jsfiddle.net/dvdsmpsn/7qusz4q3/ - so you can test it in the relevant browsers.

At time of writing, it works on the current version on Chrome, Safari, Opera, Firefox, Vivaldi & Internet Explorer 11. I've not tested it in other browsers.

Noles answered 22/6, 2016 at 23:12 Comment(5)
I change the src in that fiddle to "basicinstructions.net" which is iframeable, yet it still gives error-loading-after-5-seconds. Maybe because I don't have super speed internet?Bourne
Why? It is iframeable, no X-Frame-Options.Bourne
As jsfiddle is secure on https, iframing on http is normally blocked by the browser. That's why I added the onload after 5s test. Notice both URLs I tested with are https only.Noles
Bit late but I'm having problem with this issue too and testing with fr.wikipedia.org/wiki/Main_Page doesn't work even if the page load in the iframe.Benner
This finally solved my problem after hours of searching!Ignatius
E
1

The only thing I can think of is to proxy an AJAX request for the url, then look at the headers, and if it doesn't have X-Frame-Options, then show it in the iframe. Far from ideal, but better than nothing.

Emplane answered 21/11, 2011 at 2:16 Comment(3)
Cross-domain issues are going to stop this from working, unfortunately. Unless of course you can find or make yourself a proxy that copies over the headers.Amor
Yup, you're totally right; stupid oversight on my part. I updated my answer. Thanks for pointing this out!Emplane
This may be the closest thing to an answer although there's no telling if the proxy location is blocked from that service, or if user may be logged in to that site in which case they could not send that header. This also would likely make all requests that are checked this way before loading, over twice as slow since it is requested by the proxy.Bourne
L
0

At least in Chrome, you can notice the failure to load because the iframe.onload event doesn't trigger. You could use that as an indicator that the page might not allow iframing.

Lynnettelynnworth answered 13/2, 2012 at 0:6 Comment(1)
Chrome does fire it now.Saxophone
A
0

Online test tools might be useful. I used https://www.hurl.it/. you can clearly see the response header. Look for X-frame-option. if value is deny - It will not display in iframe. same origin- only from the same domain, allow- will allow from specific websites.

If you want to try another tool, you can simply google for 'http request test online'.

Aviate answered 1/5, 2017 at 23:49 Comment(0)
E
-1

This is how I had checked for X-Frames-Options for one of my requirements. On load of a JSP page, you can use AJAX to send an asynchronous request to the specific URL as follows:

var request = new XMLHttpRequest();
request.open('GET', <insert_URL_here>, false);
request.send(null);

After this is done, you can read the response headers received as follows:

var headers = request.getAllResponseHeaders();

You can then iterate over this to find out the value of the X-Frames-Options. Once you have the value, you can use it in an appropriate logic.

Emmalynne answered 22/6, 2016 at 7:40 Comment(3)
Same-origin policy would prevent you from requesting any arbitrary url. Unless Access-Control-Allow-Origin: * is a header which isn't likely.Emplane
I doubt that. I remember having used this for one of the bugs that I was supposed to fix in my project. The bug involved opening external links. So we had to somehow figure if the link could be opened inside an iframe or not (if it could not, we had to open it in a new tab). This is kind of the same thing which I did and as of this date its still an acceptable solution. Of course, project specific stuff may come into the picture.Emmalynne
Indeed it does not work given any arbitrary website url: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://www.openstreetmap.org/. (Reason: CORS header 'Access-Control-Allow-Origin' missing).Bourne
B
-2

This can be achieved through

a) Create a new IFrame through CreateElement

b) Set its display as 'none'

c) Load the URL through the src attribute

d) In order to wait for the iframe to load, use the SetTimeOut method to delay a function call (i had delayed the call by 10 sec)

e) In that function, check for the ContentWindow length.

f) if the length > 0, then the url is loaded else URL is not loaded due to X-Frame-Options

Below is the sample code:

function isLoaded(val) {
var elemId = document.getElementById('ctlx');
if (elemId != null)
    document.body.removeChild(elemId);


var obj= document.createElement('iframe');

obj.setAttribute("id", "ctlx");

obj.src = val;
obj.style.display = 'none';

document.body.appendChild(obj);

setTimeout(canLoad, 10000);  

}

function canLoad() {
//var elemId = document.getElementById('ctl100');
var elemId = document.getElementById('ctlx');
if (elemId.contentWindow.length > 0) {
    elemId.style.display = 'inline';

}

else {
    elemId.src = '';
    elemId.style.display = 'none';
    alert('not supported');
}
}
Byrnes answered 19/7, 2015 at 15:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.