【问题标题】:SecurityError: Blocked a frame with origin from accessing a cross-origin frameSecurityError:阻止具有源的框架访问跨域框架
【发布时间】:2014-09-25 16:58:35
【问题描述】:

我在我的 HTML 页面中加载 <iframe> 并尝试使用 Javascript 访问其中的元素,但是当我尝试执行我的代码时,我收到以下错误:

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

您能帮我找到一个解决方案,以便我可以访问框架中的元素吗?

我正在使用此代码进行测试,但徒劳无功:

$(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!";
    });
});

【问题讨论】:

标签: javascript jquery security iframe same-origin-policy


【解决方案1】:

实际上,针对特定场景有一种解决方法。

如果您有两个进程在同一个域上运行但端口不同,则两个窗口可以无限制地交互。 (即localhost:3000 & localhost:2000)。为了使这项工作正常进行,每个窗口都需要将其域更改为共享源:

document.domain = 'localhost'

这也适用于您在同一二级域上使用不同子域的情况,即您在 john.site.com 上尝试访问 peter.site.com 或只是 site.com

document.domain = 'site.com'

通过显式设置document.domain;浏览器将忽略主机名差异,并且可以将窗口视为来自“同源”。现在,在父窗口中,您可以访问 iframe:frame.contentWindow.document.body.classList.add('happyDev')

【讨论】:

    【解决方案2】:

    检查域的 Web 服务器以获取 http://www.&lt;domain&gt;.com 配置为 X-Frame-Options 这是一项旨在防止 clickJacking 攻击的安全功能,

    clickJacking 是如何工作的?

    1. 邪恶页面与受害者页面一模一样。
    2. 然后它欺骗用户输入他们的用户名和密码。

    从技术上讲,邪恶有一个iframe,其中包含受害者页面的来源。

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

    安全功能的工作原理

    如果您想阻止在 iframe 中呈现 Web 服务器请求,请添加 x-frame-options

    X-Frame-Options 拒绝

    选项有:

    1. SAMEORIGIN //只允许我自己的域在 iframe 中呈现我的 HTML。
    2. DENY //不允许我的 HTML 在任何 iframe 中呈现
    3. "ALLOW-FROM https://example.com/" //允许特定域在 iframe 中呈现我的 HTML

    这是 IIS 配置示例:

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

    问题的解决方法

    如果 web 服务器激活了安全功能,它可能会导致客户端 SecurityError,因为它应该。

    【讨论】:

    • 我不认为 X-Frame-Options 在这里适用 - 访客(嵌入式)页面定义的 X-Frame-Options 会导致父级拒绝加载页面,但就我知道它不会影响 javascript 访问 - 即使使用 X-Frame-Options: *,我认为您将无法使用 javascript 访问不同来源访客页面的 DOM
    • 这个答案实际上并没有回答问题,问题没有问这是否安全。
    【解决方案3】:

    同源政策

    不能使用 JavaScript 访问具有不同来源的 &lt;iframe&gt;,如果你能做到,那将是一个巨大的安全漏洞。对于same-origin policy 浏览器阻止脚本尝试访问具有不同来源的框架

    如果地址的以下至少一个部分没有得到维护,则认为来源不同:

    协议://主机名:端口/...

    如果您要访问框架,协议、主机名和端口必须与您的域相同。

    注意:众所周知,Internet Explorer 并未严格遵守此规则,详情请参阅here

    示例

    下面是尝试从http://www.example.com/home/index.html 访问以下 URL 时会发生的情况

    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 
    

    解决方法

    即使同源策略阻止脚本访问具有不同来源的站点的内容,如果您拥有这两个页面,则可以使用window.postMessage 及其相关的message 事件解决此问题 在两个页面之间发送消息,如下所示:

    • 在您的主页中:

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

      postMessage() 的第二个参数可以是'*',表示对目的地的来源没有偏好。应尽可能提供目标来源,以避免泄露您发送到任何其他网站的数据。

    • 在您的&lt;iframe&gt;(包含在主页中):

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

    此方法可以双向应用,也可以在主页中创建一个监听器,并接收来自框架的响应。同样的逻辑也可以在弹出窗口中实现,基本上任何由主页生成的新窗口(例如使用window.open())也可以实现,没有任何区别。

    您的浏览器中禁用同源策略

    已经有一些关于这个主题的好答案(我刚刚发现他们在谷歌上搜索),所以,对于可能的浏览器,我将链接相关答案。但是,请记住禁用同源策略只会影响您的浏览器。此外,在禁用同源安全设置的情况下运行浏览器会授予 任何 网站对跨源资源的访问权限,因此 这是非常不安全的,如果您不确切知道自己是什么,则永远不要这样做做(例如开发目的)

    【讨论】:

    • 我找到的任何其他答案 12 表明 CORS/Access-Control-Allow-Origin 不适用于 iFrame,仅适用于 XHRs, Fonts, WebGL and canvas.drawImage。我相信postMessage 是唯一的选择。
    • @ccppjava 你不需要===,你已经知道变量类型是字符串,所以===在这里没用。
    • @SabaAhang 只需检查iframe.src,如果该站点与您域的主机名不同,则您无法访问该框架。
    • @user2568374 这是一个糟糕的主意。如果您检查event.origin.indexOf(location.ancestorOrigins[0]),您基本上是在允许任何父框架访问您的框架,正如您可以想象的那样,这是一个非常糟糕的主意。
    • @user2568374 location.ancestorOrigins[0] 是父框架的位置。如果您的框架在另一个站点内运行并且您使用event.origin.indexOf(location.ancestorOrigins[0])进行检查,则您正在检查事件的来源是否包含父框架的地址,始终是true,因此您允许具有 任何来源any parent 访问您的框架,这显然不是您想要做的事情。此外,document.referrer 也是不好的做法,正如我在上面的 cmets 中已经解释的那样。
    【解决方案4】:

    我在尝试嵌入 iframe 然后使用 Brave 打开网站时遇到此错误。当我将相关站点的“Shields Down”更改为“Shields Down”时,该错误消失了。显然,这不是一个完整的解决方案,因为任何使用 Brave 访问该站点的其他人都会遇到同样的问题。要真正解决它,我需要做此页面上列出的其他事情之一。但至少我现在知道问题出在哪里了。

    【讨论】:

      【解决方案5】:

      如果您可以控制 iframe 的内容 - 也就是说,如果它只是在跨域设置中加载,例如在 Amazon Mechanical Turk 上 - 您可以使用内部 html 的 &lt;body onload='my_func(my_arg)'&gt; 属性来规避这个问题.

      例如,对于内部 html,使用this html 参数(是的 - 定义了this,它指的是内部主体元素的父窗口):

      &lt;body onload='changeForm(this)'&gt;

      在内部 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>
      

      【讨论】:

        【解决方案6】:

        我想添加可以对此产生影响的 Java Spring 特定配置。

        在网站或网关应用程序中有一个 contentSecurityPolicy 设置

        在 Spring 中你可以找到 WebSecurityConfigurerAdapter 子类的实现

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

        ...

        .referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)
        

        如果您没有在此处定义安全的外部内容,浏览器将被阻止。

        【讨论】:

          【解决方案7】:
          • 打开开始菜单
          • 键入 windows+R 或打开“运行”
          • 执行以下命令。

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

          【讨论】:

          • 对于任何不是快速和肮脏的测试的东西都很糟糕......并且已经在接受的答案中解决了。
          • 即使使用该命令,它也不起作用,因为 Chrome 会避免以这种方式禁用网络安全
          【解决方案8】:

          对我来说,我想实现 2 次握手,意思是:
          - 父窗口的加载速度将比 iframe
          - iframe 应在准备好后立即与父窗口对话
          - 父级已准备好接收 iframe 消息并重播

          此代码用于使用[CSS custom property]在 iframe 中设置白标
          代码:
          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","*");
              }
          });
          

          父母

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

          您当然可以限制来源和文本,这是易于使用的代码
          我发现这个例子很有帮助:
          [Cross-Domain Messaging With postMessage]

          【讨论】:

          • 我正在处理 safari 的问题,其中 iframe 中的文档执行其 JS 的时间晚于父页面,这导致消息在 iframe 中的文档正在侦听消息之前发送;这与 chrome 和 firefox 的做法完全相反——你在 ios 上的 safari 中测试过你的代码吗?顺便说一句,第二个参数值为“*”的 postMessage 不太安全,您应该始终指定域
          • 您的第一个代码块是在父级的 iframe 上还是在加载到 iframe 的页面上?
          【解决方案9】:

          补充 Marco Bonelli 的回答:当前在框架/iframe 之间进行交互的最佳方式是使用 window.postMessagesupported by all browsers

          【讨论】:

          • window.postMessage 只有在我们能够访问父元素(我们的 HTML 页面)和子元素(其他域 iframe)时才能使用。否则“没有可能”,它总是会抛出一个错误“未捕获的 DOMException:阻止具有源“yourdomainname.com>”的框架访问跨域框架。”
          猜你喜欢
          • 2019-09-17
          相关资源
          最近更新 更多