Foolproof way to detect if iframe is cross domain

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.

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.

Read More:   Serializing and unserializing an array in javascript

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

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}
    ` 
}

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
      }
    }
});

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
 }
}


The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .

Similar Posts