Search code examples
javascriptiframecross-domainpostmessage

Call function on iframe parent window (window.top/window.parent) (circumvent cross-origin object access security)


Is there a way to circumvent the cross-origin security error that comes up when an iframe (domain A) on a page (domain B) tries to access properties of window.top?

I want to call a function, ie. someFunc, which belongs to domain B, through an event in the iframe of domain A, in the context of domain B.

Example:

a.com/index.html

<!DOCTYPE html>
<html>
  <body>
    <script>
      var someFunc = t => document.querySelector("#test").text = t;
    </script>
    <span id="test">This is some dummy text.</span>
    <iframe src="https://b.com/index.html"></iframe>
  </body>
</html>

b.com/index.html

<!DOCTYPE html>
<html>
  <body>
    <button id="btn">Test</button>
    <script>
      document.querySelector("#btn").addEventListener("click", (e) => {
        window.top.someFunc("This text has been changed through a cross-origin")
      });
    </script>
  </body>
</html>

This example raises the following error on Firefox:
SecurityError: Permission denied to access property "someFunc" on cross-origin object


Solution

  • The correct way to circumvent this is using Window.postMessage - Essentially, send a message from the iframe up to its parent window, which, if it has an event listener for "message", can react accordingly.

    ie.:

    a.com/index.html

    <!DOCTYPE html>
    <html>
      <body>
        <script>
          let someFunc = t => document.querySelector("#test").text = t;
          window.addEventListener("message", event => someFunc(e.data));
        </script>
        <span id="test">This is some dummy text.</span>
        <iframe src="https://b.com/index.html"></iframe>
      </body>
    </html>
    

    b.com/index.html

    <!DOCTYPE html>
    <html>
      <body>
        <button id="btn">Test</button>
        <script>
          document.querySelector("#btn")
            .addEventListener("click", event => {
              window.parent.postMessage("Changed text", "http://a.com");
            });
        </script>
      </body>
    </html>