23

I'm trying to determine if any iframe is cross-domain or not. According to the accepted answer in this question: Detect when iframe is cross-domain, then bust out of it it says to put the code accessing the contentDocument of the iframe in a try / catch block. I tried this for a cross-domain iframe in Chrome:

try { 
  document.getElementsByTagName('iframe')[0].contentDocument;
} catch(err){
  console.log("called");
}

and it still throws the cross-domain error and does not catch the error.

I also tried to check if the protocol + host + port of the parent page url is in the src of the iframe:

function thirdPartyIframe(iframe){
  var url = document.location.protocol + "//" + document.location.hostname + (document.location.port === "" ? "" : ":" + document.location.port);
  var regexp = new RegExp(url.replace(/\//g, "\\/").replace(/\./g, "\\."));
  if (iframe.src === '' || regexp.test(iframe.src)){
    return false;
  } else {
    return true;
  }
}

but this does not seem to work for the first iframe on the homepage of Facebook with src equal to (it's long):

"http://www.facebook.com/ai.php?aed=AQLlH2cfdnsnLrDUVyqrQPlWpayw9N09Z_iNuhulevbeEfVa4mcVRcT8cjAZOjQb8y1QXab5Ae3aSEJx49U_Qv35rtSp1VC9cY0_CrpOjMDk40hS_Xm57A996YtRVCcWSuRZ_jZERQ_iA_E4621NAbKHT9dsB7uElkRtTvl5K-zPI0jeH-BEnlZIOXbeEdbRC6qCwoToaltolpe-Ot2KWgkfb_vBZYpzUc3jQoEHzLG6tauO9l_hkXpYpHxnt-KYFKIFZ1PgmrHgb0UcGjeKHl7yBR1AbW2n5XgdgaAhFvBjs5GZlKy566nvl8eLRA60orwkwtWYeN8-gKoAmOLm7-6minsWn8lk1h2Qn3p07HCTSnYHfv1aJ6mF5jmuzP0YYe7Ym9ZbmK-tvax4uPAQJ2OdULilKbEh8M-2V9pVY3AC228OPlrRullZuuOg8DI2A8WeMF-fbbOdOFFVCe5Gj1CaZu3LYXiqdG7mUgY6AEpk9ZzGT4fC2K8DInQo1AypCvzG64C_bEWfODeXe0aGbkWwsUUmO7E5HFg0tvZkK5IAR_xxxQ2rlf5jbcEDo_2gdIDdHe1HT75-SJLUsSA0M8EU01oNNPuWwEC2BW6inepc9QPuqeg42tcEbKLU-rIUnXDBLvgutft8azWPPQ6_LafGjCAmC9uTalagoWLLDMpQOThvPg7YeVd7qg_c9Mzn2GAfuswcxDSxyRIZo9MaOhA6mhfXeE1tmjyBBsMxnx08tO21Jsfgch59fmMxpeJzdsNMPK3FAojfglvCQ2Zrt_6ataexUB4xlM7_PhKrfBPtxb5fe2TE9-nlWruNEpoCrzI05yv4Go3CYEWHob06K_9iICfNVTFkSYGTiJnMXCy_fdgfyzUIn5QJIPRo4-Wnyg444zKAO_nyFW59LqbIanHVfFY6ybiA6KeC3meREWzTPSsrU5d_NbYHlJWb8uPNDR04jaH5e2qiYj3Y8qgLQA5m"

My function classifies it as not a third party iframe, but Chrome still throws the cross-domain error when I access its contentDocument.

I'm looking for a foolproof, cross-browser way to do this.

Community
  • 1
  • 1
user730569
  • 3,940
  • 9
  • 42
  • 68

5 Answers5

24

You need to do a little more than what's in your try/catch to handle different browsers and to handle different ways that browsers deal with cross domain access:

function canAccessIFrame(iframe) {
    var html = null;
    try { 
      // deal with older browsers
      var doc = iframe.contentDocument || iframe.contentWindow.document;
      html = doc.body.innerHTML;
    } catch(err){
      // do nothing
    }

    return(html !== null);
}

In your example, this would be:

var accessAllowed = canAccessIFrame(document.getElementsByTagName('iframe')[0]);

Working demo: http://jsfiddle.net/jfriend00/XsPL6/

Tested in Chrome 21, Safari 5.1, Firefox 14, IE7, IE8, IE9.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • 1
    ok this seems to work but it still throws an error in the console in chrome, i'm guessing it doesn't matter though – user730569 Sep 12 '12 at 05:15
  • Yes, it throws the error, but the error is caught by your code so it does not matter. That's how you find out if you can access it or not. – jfriend00 Sep 12 '12 at 05:22
  • 4
    Chrome throws and logs its own error, seemingly uncatchable by this snippet's try..catch. I would say it does matter. I don't want to spam the console with errors, when I safely test iframe access and workaround it. – Bluu Sep 06 '13 at 20:18
  • 1
    What would be a safe equivalent of this going the other direction, from the embedded content testing if the parent is cross-domain or same domain? I've asked this as a separate question if anyone is interested: [Foolproof way to detect if this page is INSIDE a cross-domain iframe](http://stackoverflow.com/questions/21751377/foolproof-way-to-detect-if-this-page-is-inside-a-cross-domain-iframe) – user56reinstatemonica8 Feb 13 '14 at 10:24
  • This seems not to work if you try to embed an https website which is strange... – Hexodus Jan 29 '21 at 14:42
  • yeah this does not work at all – strix25 Jan 17 '22 at 11:36
7

A more shorter and more readable function for modern browsers

function canAccessIframe(iframe) {
  try {
    return Boolean(iframe.contentDocument);
  }
  catch(e){
    return false;
  }
}

Tested with Chrome 79 and Firefox 52 ESR.

Explanation: you can check any iframe property that is not accessible cross-origin and convert to boolean. example: contentDocument / contentWindow.document / location.href / etc.

Boolean docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean

crisc2000
  • 1,082
  • 13
  • 19
0

This is a shorter version that combines the both approches with ES6. Please notice that it only works if your domain and the iframed domains have the same security type. Change in this example the google link to be http and it won't work.

let canAccessIFrame = (iframe) => {
       return !!(iframe.contentDocument)
}

let checkIFrameAccess = () => {
        let result = document.getElementById('result')
        let remote = canAccessIFrame(document.getElementById('remote'))
        let local = canAccessIFrame(document.getElementById('local'))
    
    result.innerHTML = `
        Remote iframe access: ${remote}
        <br>Local iframe access:  ${local}
    ` 
}
Hexodus
  • 12,361
  • 6
  • 53
  • 72
0

I did it this way with jquery. It may be not tidy 100%, but works for me.

$(iframe).load(function (e) {
  try
    {
    // try access to check
    console.log(this.contentWindow.document);
    }
  catch (e)
    {
    console.log(e);
    var messageLC = e.message.toLowerCase();
    if (messageLC.indexOf("x-frame-options") > -1 || messageLC.indexOf('blocked a frame with origin') > -1 || messageLC.indexOf('accessing a cross-origin') > -1)
      {
      // show Error Msg with cause of cross-origin access denied
      }
    else
      {
      // Shoe Error Msg with other cause
      }
    }
});
Ruwen
  • 3,008
  • 1
  • 19
  • 16
0

I was having issues with my IFrame returning an empty string until it was loaded. So I started using the following:

function canAccessIFrame(iframe) {
 try { 
   const doc = iframe.contentDocument || iframe.contentWindow.document;
   const html = doc.body.innerHTML;
   return html !== null && html !== ''
 } catch(err){
   return false
 }
}
Steven Spungin
  • 27,002
  • 5
  • 88
  • 78