649

I am loading an <iframe> in my HTML page and trying to access the elements within it using Javascript, but when I try to execute my code, I get the following error:

SecurityError: Blocked a frame with origin "http://www.<domain>.com" from accessing a cross-origin frame.

Can you please help me to find a solution so that I can access the elements in the frame?

I am using this code for testing, but in vain:

$(document).ready(function() {
    var iframeWindow = document.getElementById("my-iframe-id").contentWindow;

    iframeWindow.addEventListener("load", function() {
        var doc = iframe.contentDocument || iframe.contentWindow.document;
        var target = doc.getElementById("my-target-id");

        target.innerHTML = "Found it!";
    });
});
Marco Bonelli
  • 48,251
  • 16
  • 95
  • 101
mubashermubi
  • 7,066
  • 4
  • 14
  • 28

8 Answers8

943

Same-origin policy

You can't access an <iframe> with different origin using JavaScript, it would be a huge security flaw if you could do it. For the same-origin policy browsers block scripts trying to access a frame with a different origin.

Origin is considered different if at least one of the following parts of the address isn't maintained:

protocol://hostname:port/...

Protocol, hostname and port must be the same of your domain if you want to access a frame.

NOTE: Internet Explorer is known to not strictly follow this rule, see here for details.

Examples

Here's what would happen trying to access the following URLs from http://www.example.com/home/index.html

URL                                             RESULT 
http://www.example.com/home/other.html       -> Success 
http://www.example.com/dir/inner/another.php -> Success 
http://www.example.com:80                    -> Success (default port for HTTP) 
http://www.example.com:2251                  -> Failure: different port 
http://data.example.com/dir/other.html       -> Failure: different hostname 
https://www.example.com/home/index.html:80   -> Failure: different protocol
ftp://www.example.com:21                     -> Failure: different protocol & port 
https://google.com/search?q=james+bond       -> Failure: different protocol, port & hostname 

Workaround

Even though same-origin policy blocks scripts from accessing the content of sites with a different origin, if you own both the pages, you can work around this problem using window.postMessage and its relative message event to send messages between the two pages, like this:

  • In your main page:

    const frame = document.getElementById('your-frame-id');
    frame.contentWindow.postMessage(/*any variable or object here*/, 'http://your-second-site.com');
    

    The second argument to postMessage() can be '*' to indicate no preference about the origin of the destination. A target origin should always be provided when possible, to avoid disclosing the data you send to any other site.

  • In your <iframe> (contained in the main page):

    window.addEventListener('message', event => {
        // IMPORTANT: check the origin of the data! 
        if (event.origin.startsWith('http://your-first-site.com')) { 
            // The data was sent from your site.
            // Data sent with postMessage is stored in event.data:
            console.log(event.data); 
        } else {
            // The data was NOT sent from your site! 
            // Be careful! Do not use it. This else branch is
            // here just for clarity, you usually shouldn't need it.
            return; 
        } 
    }); 
    

This method can be applied in both directions, creating a listener in the main page too, and receiving responses from the frame. The same logic can also be implemented in pop-ups and basically any new window generated by the main page (e.g. using window.open()) as well, without any difference.

Disabling same-origin policy in your browser

There already are some good answers about this topic (I just found them googling), so, for the browsers where this is possible, I'll link the relative answer. However, please remember that disabling the same-origin policy will only affect your browser. Also, running a browser with same-origin security settings disabled grants any website access to cross-origin resources, so it's very unsafe and should NEVER be done if you do not know exactly what you are doing (e.g. development purposes).

Marco Bonelli
  • 48,251
  • 16
  • 95
  • 101
  • 37
    Any other answer I've found [1](http://stackoverflow.com/a/22413275), [2](http://stackoverflow.com/a/23363050), suggests that CORS/`Access-Control-Allow-Origin` does not apply to iFrames, only to [XHRs, Fonts, WebGL and `canvas.drawImage`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS). I believe `postMessage` is the only option. – snappieT Jan 14 '15 at 12:12
  • what if the origin is: http://yousite.com.mysite.com ? should we use === instead? – ccppjava Aug 03 '15 at 11:04
  • 3
    @ccppjava you don't need the ===, you already know that the variable type it's a string, so === is useless here. – Marco Bonelli Aug 03 '15 at 16:22
  • How to avoid the exception by skipping the access that causes it in the first place? to be more specific I have a code that runs against the `window` object of a given page to inspect elements and on some pages the script is interrupted due to this error. I have no intention of accessing any references to other windows/iframes and want to safely ignore them in my code but haven't found a way to do so! any suggestions? – Saba Ahang Oct 17 '15 at 02:21
  • 4
    @SabaAhang just check for the `iframe.src`, and if the site it's different from your domain's hostname then you can't access that frame. – Marco Bonelli Oct 17 '15 at 09:34
  • @MarcoBonelli, When using window.postMessage Workaround, what are the domain protocol restrictions? For Instance, is it possible to post message from an https origin to an http target iframe and vice-versa? – Juliomac Mar 16 '16 at 13:11
  • @Juliomac of course, as long as you own both domains you can set a sender on the main one and a receiver on the dimain in the frame. By the way the browser may warn you or (worst case) block the frame from loading if it'a http loading in an https page, you may want to check on that first. – Marco Bonelli Mar 16 '16 at 13:17
  • For simple cases another workaround would be to pass GET parameters as part of the `src` and extract them on the recieving side through `location` object, ex `` – Daniel Sokolowski May 20 '16 at 16:59
  • After almost cry, I check @parliament suggestion and the **porthole library** works excellent. If anyone is struggling with this issue I will recommend to use it. The other ways are quite messy to implement and prone to errors. I implemented with Asp.Net Mvc with out problems. – Juan Acosta Jun 27 '16 at 06:53
  • @Redzarf: I think we can avoid not and != 1. We can put the body in else part to if part and put body of if into else part. – Vinayak Kaniyarakkal Jan 27 '17 at 07:01
  • http://stackoverflow.com/questions/29983786/blocked-a-frame-of-origin-null-from-accessing-a-cross-origin-frame-chrome check it out – jewelhuq Feb 19 '17 at 01:25
  • Here is the exact [list of data types](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) that can be passed as the `message` parameter into `postMessage()`, although "any variable or object" is a reasonable summary. Also, `if(document.referrer.indexOf(event.origin) == 0)` is a dynamic way to check whether a message to an iframe came from its parent window. – Jamie Birch Apr 27 '17 at 00:08
  • Actually, `document.referrer.indexOf(event.origin) == 0` is **not** a dynamic way to check whether a message to an ` – Marco Bonelli Apr 27 '17 at 09:49
  • @MarcoBonelli Hi, I am using instead of – Ng2-Fun May 18 '17 at 21:22
  • @Ng2-Fun `` has nothing to do with frames. – Marco Bonelli May 18 '17 at 21:30
  • How do you explain this: https://gyazo.com/caf4dda723bc0afd0baa058172d91573 – undefined Feb 25 '18 at 11:03
  • @MarcoBonelli it's blocking a frame from the same origin. Here is what you said: http://www.example.com/home/other.html -> Success http://www.example.com/dir/inner/another.php -> Success – undefined Feb 25 '18 at 11:40
  • @ZeusZdravkov Sorry I misread the URLs, okay they seem to have the same origin, but the origin is not the same thing that can block a frame from being loaded. There's not much to say without seeing more of the server and client side of that page. You should post a more detailed question. – Marco Bonelli Feb 25 '18 at 11:44
  • Instead of "h t t p ://yoursite.com", you may be able to use location.ancestorOrigins[0] instead to avoid hardcode. – user2568374 May 16 '18 at 19:54
  • 1
    @user2568374 **that's a terrible idea**. If you check for `event.origin.indexOf(location.ancestorOrigins[0])` you are basically allowing any parent frame to access your frame, and as you can imagine, that's a very bad idea. – Marco Bonelli May 16 '18 at 20:07
  • @Marco Bonelli Please explain "any" and "bad". I have one parent frame that needs to be accessed. There are no more than 1. the iFrame posts a message to this one parent. This is good. And by the way, I actually changed location.ancestorOrigins[0] which worked with Chrome to document.referrer which works for all browsers. – user2568374 May 22 '18 at 12:30
  • 2
    @user2568374 `location.ancestorOrigins[0]` is the location of the parent frame. If your frame is running inside *another site* and you check using `event.origin.indexOf(location.ancestorOrigins[0])` you are checking if the origin of the event contains the parent's frame address, **which is always going to be `true`**, therefore you are allowing *any parent* with *any origin* to access your frame, and this is obviously not something you want to do. Moreover, `document.referrer` is bad practice too, as I already explained in the comments above. – Marco Bonelli May 22 '18 at 13:39
  • I'm surprised this entire page doesn't mention `document.domain`. – Jim W says reinstate Monica Oct 02 '19 at 17:53
  • @JimW that's because it's only the hostname, it doesn't cover the protocol.You wouldn't want, for example, to allow messages from an `http://` page if your frame is under `https://` (and possibly even vice-versa). – Marco Bonelli Oct 02 '19 at 18:26
  • If you are using an ``object`` instead of an ``iframe`` and want to access the postMessage function of it in the Internet Explorer you must go this way ``document.getElementById('youtObjectId').contentDocument.defaultView.postMessage(message, origin);``. This will not work in the Chrome browser – FleMo Jan 03 '20 at 10:49
  • Can I apply the same principle when developing a chrome extension as isolated iframe? As far as I understand I could write the first code snippet inside my 'content.js' script and second code snippet inside the 'app.js' from my 'iframe'. Is that right? – Niklas Weber Jan 13 '21 at 10:27
  • If you got `Property 'contentWindow' does not exist on type 'HTMLElement'` just follow the answer [here](https://stackoverflow.com/questions/38457662/iframe-inside-angular2-component-property-contentwindow-does-not-exist-on-typ/46629313) – ViniciusArruda May 21 '21 at 13:35
56

Complementing Marco Bonelli's answer: the best current way of interacting between frames/iframes is using window.postMessage, supported by all browsers

Geert
  • 2,767
  • 1
  • 17
  • 16
  • 14
    window.postMessage we can use only if we can able to access both parent(our HTML page) and children element(other domain iframe).Otherwise "THERE IS NO POSSIBILITY", it will always throws an error "Uncaught DOMException: Blocked a frame with origin "" from accessing a cross-origin frame." – VIJAY P Feb 01 '17 at 18:35
18

Check the domain's web server for http://www.<domain>.com configuration for X-Frame-Options It is a security feature designed to prevent clickJacking attacks,

How Does clickJacking work?

  1. The evil page looks exactly like the victim page.
  2. Then it tricked users to enter their username and password.

Technically the evil has an iframe with the source to the victim page.

<html>
    <iframe src='victim_domain.com'/>
    <input id="username" type="text" style="display: none;"/>
    <input id="password" type="text" style="display: none;"/>
    <script>
        //some JS code that click jacking the user username and input from inside the iframe...
    <script/>
<html>

How the security feature work

If you want to prevent web server request to be rendered within an iframe add the x-frame-options

X-Frame-Options DENY

The options are:

  1. SAMEORIGIN //allow only to my own domain render my HTML inside an iframe.
  2. DENY //do not allow my HTML to be rendered inside any iframe
  3. "ALLOW-FROM https://example.com/" //allow specific domain to render my HTML inside an iframe

This is IIS config example:

   <httpProtocol>
       <customHeaders>
           <add name="X-Frame-Options" value="SAMEORIGIN" />
       </customHeaders>
   </httpProtocol>

The solution to the question

If the web server activated the security feature it may cause a client-side SecurityError as it should.

Xaver
  • 9,590
  • 11
  • 47
  • 84
Shahar Shokrani
  • 4,735
  • 5
  • 31
  • 55
  • 2
    I don't think that X-Frame-Options applies here - X-Frame-Options defined by the guest (embedded) page can cause the parent to refuse to load the page, but as far as I know it doesn't affect javascript access - even with X-Frame-Options: *, I don't think you'll be able to access the DOM of a different origin guest page with javascript – Noah Gilmore Nov 05 '19 at 14:54
16

For me i wanted to implement a 2-way handshake, meaning:
- the parent window will load faster then the iframe
- the iframe should talk to the parent window as soon as its ready
- the parent is ready to receive the iframe message and replay

this code is used to set white label in the iframe using [CSS custom property]
code:
iframe

$(function() {
    window.onload = function() {
        // create listener
        function receiveMessage(e) {
            document.documentElement.style.setProperty('--header_bg', e.data.wl.header_bg);
            document.documentElement.style.setProperty('--header_text', e.data.wl.header_text);
            document.documentElement.style.setProperty('--button_bg', e.data.wl.button_bg);
            //alert(e.data.data.header_bg);
        }
        window.addEventListener('message', receiveMessage);
        // call parent
        parent.postMessage("GetWhiteLabel","*");
    }
});

parent

$(function() {
    // create listener
    var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
    var eventer = window[eventMethod];
    var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
    eventer(messageEvent, function (e) {
        // replay to child (iframe) 
        document.getElementById('wrapper-iframe').contentWindow.postMessage(
            {
                event_id: 'white_label_message',
                wl: {
                    header_bg: $('#Header').css('background-color'),
                    header_text: $('#Header .HoverMenu a').css('color'),
                    button_bg: $('#Header .HoverMenu a').css('background-color')
                }
            },
            '*'
        );
    }, false);
});

naturally you can limit the origins and the text, this is easy-to-work-with code
i found this examlpe to be helpful:
[Cross-Domain Messaging With postMessage]

Yakir Manor
  • 4,409
  • 1
  • 28
  • 24
  • i'm dealing with an issue with safari where document in iframe is executing its JS later than parent page which causes the message to be sent earlier than document in iframe is listening to messages; which is exactly opposite from what chrome and firefox do - have you tested your code in safari on ios? btw postMessage with second parameter of value "*" is not quite safe, you should always specify domain – sKopheK Mar 22 '18 at 15:51
  • Your first block of code, is that on the iframe in the parent or is it on the page that gets loaded into the iframe ? – Demonic218 Feb 15 '19 at 09:45
0

I would like to add Java Spring specific configuration that can effect on this.

In Web site or Gateway application there is a contentSecurityPolicy setting

in Spring you can find implementation of WebSecurityConfigurerAdapter sub class

contentSecurityPolicy("
script-src 'self' [URLDomain]/scripts ; 
style-src 'self' [URLDomain]/styles;
frame-src 'self' [URLDomain]/frameUrl...

...

.referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)

Browser will be blocked if you have not define safe external contenet here.

ssp
  • 61
  • 1
  • 4
0

If you have control over the content of the iframe - that is, if it is merely loaded in a cross-origin setup such as on Amazon Mechanical Turk - you can circumvent this problem with the <body onload='my_func(my_arg)'> attribute for the inner html.

For example, for the inner html, use the this html parameter (yes - this is defined and it refers to the parent window of the inner body element):

<body onload='changeForm(this)'>

In the inner html :

    function changeForm(window) {
        console.log('inner window loaded: do whatever you want with the inner html');
        window.document.getElementById('mturk_form').style.display = 'none';
    </script>
Zhanwen Chen
  • 910
  • 10
  • 18
0

I experienced this error when trying to embed an iframe and then opening the site with Brave. The error went away when I changed to "Shields Down" for the site in question. Obviously, this is not a full solution, since anyone else visiting the site with Brave will run into the same issue. To actually resolve it I would need to do one of the other things listed on this page. But at least I now know where the problem lies.

Nick K9
  • 2,224
  • 1
  • 17
  • 39
-35
  • Open the start menu
  • Type windows+R or open "Run
  • Execute the following command.

chrome.exe --user-data-dir="C://Chrome dev session" --disable-web-security

Amsakanna
  • 9,958
  • 5
  • 43
  • 53
  • 10
    Terrible for anything that is not a quick and dirty test … and already address in the accepted answer. – Quentin Mar 26 '19 at 11:23
  • 3
    Even with the command, it doesn´t work because Chrome avoids disabling the web security this way – Metafaniel Jul 03 '19 at 15:56