浏览器同源策略详解

web 开发中经常会遇到 ajax 请求无法发送,cookie 无法读取,资源无法加载等问题,这背后很可能是受到了浏览器同源策略的限制。在此梳理一下浏览器的同源安全策略,为解决问题提供便利。

同源的定义

一个完整的 url 地址,例如 http://www.site.com:8080/home/index.html?q=123#home 由以下几部分组成:

  • 协议(protocol):http:
  • 域名(domain):www.site.com
  • 端口(port):8080
  • 路径(pathname):/home/index.html
  • 查询(search):?q=123
  • 锚点(hash):#home

这里有几点要注意:

  • 域名分为一级域名,二级域名,三级域名等,例如 www.site.com 是一个二级域名,它对应的一级域名是 site.com,也说 site.comwww.site.com 的父域。同理 web.www.site.com 是一个三级域名, www.site.com 是它的父域,site.com 也是它的父域名。
  • 端口号大部分时候会省略,这时使用协议默认的端口号,例如 http 是 80,https 是 443。
  • 路劲也有父子级关系,例如 //home 的父路径,若省略路径则表示访问根路径 /

协议 + 域名 + 端口 就组成了这个 url 的源(origin),例如上面的 url 的源是 http://www.site.com:8080,同源就是指两个 url 的源相同。下面是一些比较的例子:

http://www.site.com

https://www.site.com #不同源,协议不同
https://site.com #不同源 协议不同,域名不同
http://59.60.61.62 #不同源 域名不同
http://www.site.com:8080 #不同源 端口不同 
http://www.site.com/home/index.html?a=123#title #同源

同源策略对 iframe 的影响

iframe 为 web 提供了很多方便和灵活,但也最可能带来安全问题。因此在 iframe 的访问上浏览器严格执行同源检查,即只能访问与自己同源的 iframe 的内容。

这样会给 iframe 之间通信带来麻烦,但也有一些方法可以实现 iframe 之间的通信。在同父域名的情况下可以通过用 js 设置 domain 为父域名来允许相互访问访问。

<!-- http://www.site.com/index.html -->
<html>
    <head>
        <script>
            document.domain = 'site.com';
        </script>
    </head>
    <body>
        <iframe src="http://sub.site.com/iframe.html"></iframe>        
    </body>
</html>
<!-- http://sub.site.com/iframe.html -->
<html>
    <head>
        <script>
            document.domain = 'site.com';
        </script>
    </head>
    <body></body>
</html>

父域名也不相同的情况下只能通过 window.postMessage 通信,信息收发时最好验证一下源,不然可能被钓鱼网站利用。

<!-- http://www.site.com/index2.html -->
<html>
    <head>
    </head>
    <body>
        <iframe id="iframe" src="http://sub.site.com/iframe.html"></iframe>

        <script>
            var iframe = document.getElementById('iframe').contentWindow;
            setTimeout(function() {
                // 给不同源窗口发送消息,第二个参数为要验证的源,只有发给窗口的源是设置的值时才会发送
                // 不想验证时可以设置为 * 但有很大的安全风险
                iframe.postMessage({data: 'some data'}, 'http://sub.site.com'); 
            }, 1000);
        </script>
    </body>
</html>
<!-- http://sub.site.com/iframe2.html -->
<html>
    <head>
    </head>
    <body>
        <script>            
            // 接收消息,注意读取消息时最好验证一下消息发送者的源以保证消息可信
            window.addEventListener('message',function(event) {
                if (event.origin == 'http://www.site.com') { // 验证消息发送者的源来保证消息可信
                    console.log(event.data);
                }
            })
        </script>
    </body>
</html>

同源策略对 ajax 的影响

ajax 请求同样进行严格的同源检查,即 ajax 只能请求与当前页面同源的地址。这里要注意一下 js 文件引入到页面之后就相当于写在页面上了,即引入的 js 文件中的所有 ajax 请求都按照当前页的源来进行同源检测,而不是 js 文件的 src 地址。

当然也有方法让页面请求非同源的地址,一种方式是在拥有服务端权限时通过服务端设置 http 头来允许非同源的访问。

<!-- http://www.site.com/ajax.html -->
<html>
    <head>
        <script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script>
    </head>
    <body>
        <script>           
            $.get('http://sub.site.com/ajax.php', function(data) {
                console.log(data);
            })
        </script>
    </body>
</html>
<?php
    # http://sub.site.com/ajax.php

    // 可接受的 ajax 非同源请求,可配多个用空格分开,也可以使用 * 表示接受所有,当使用 * 时 ajax 请求无法带上 cookie
    header('Access-Control-Allow-Origin:http://www.site.com'); 
    
    echo 'ajax data';
>

注意之前提到的通过 js 设置 document.domain 的方法只对 iframe 有效,对于 ajax 请求只会按照 url 来进行检测。即无法通过设置 document.domain='site.com' 来让 http://sub.site.com ajax 访问 http://site.com

同源策略对 cookie 的影响

cookie 访问同样也有限制,但遵循的是另一套规则。在设置 cookie 时可以置顶 cookie 的域名(domain)和路径(pathname),域名只能设置为当前域或者当前域的父域,路径也只能设置为当前路径或者当前路径的父路径。读取时只能读取域名与当前域名相同或为当前域名父域,并且路径与当前路径相同或为当前路径父路径的 cookie。

<!-- http://www.site.com/cookie.html -->
<html>
    <head>
        <script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script>
        <script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js"></script>
    </head>
    <body>
        <script>
            // jq 设置 cookie,默认设置为当前域名和当前路径
            $.cookie("example", "foo"); 

            // 将 cookie 设置为一级域名和根路径以得到最大可访问性
            $.cookie("example2", "foo", 'site.com', {path: '/', domain: 'site.com'}); 

            // 只能读取域名与当前域名相同或为当前域名父域,并且路径与当前路径相同或为当前路径父路径的 cookie
            $.cookie("example2");
        </script>
    </body>
</html>

注意同源的 ajax 请求会带上 cookie,但通过之前提到的服务端设置 http 头来允许非同源 ajax 请求时,请求默认是不带 cookie 的,要带 cookie 还需要一些配置:

<!-- http://www.site.com/ajax.html -->
<html>
    <head>
        <script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script>
    </head>
    <body>
        <script>
            // jquery 设置 ajax 跨域请求带上 cookie
            $.ajaxSetup({crossDomain: true, xhrFields: {withCredentials: true}});
            
            $.get('http://sub.site.com/ajax.php', function(data) {
                console.log(data);
            })
        </script>
    </body>
</html>
<?php
    # http://sub.site.com/ajax.php

    // 可接受的 ajax 非同源请求,可配多个用空格分开,也可以使用 * 表示接受所有,当使用 * 时 ajax 请求不可以带上 cookie
	header('Access-Control-Allow-Origin:http://www.site.com'); 

    // 接受 ajax 非同源请求带 cookie
    // 前端对应要配置 xmlhttp.withCredentials = true; 才可实现 ajax 带cookie
    // cookie 的携带规则与页面访问 cookie 规则相同
	header('Access-Control-Allow-Credentials:true'); 
    
    echo 'ajax data';
>

同源策略的其他影响

  • 通过 src 属性引入资源例如图片,js,css 等不受同源策略的限制。但有一点,https 协议的页面不能加载 src 为 http 的资源,为了兼容可以在写 src 地址时不写具体协议而用 // 来匹配当前页协议。例如 <script src="//www.site.com/index.js"></script>

  • 引入不同源的 js 时,使用 window.onerror 无法捕获到其产生错误的具体信息,只会得到 script error

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值