同源策略详解

什么是同源策略

  • 同源策略是Netscape提出的一个著名的安全策略;
  • 同源策略是浏览器最核心最基础的安全策略;
  • 现在所有的可支持javascript的浏览器都会使用这个策略;
  • web构建在同源策略基础之上,浏览器对非同源脚本的限制措施是对同源策略的具体实现;
  • 本文详细说明了同源策略的含义,用具体的例子让您深入理解同源策略,并且对跨域访问的几种常用机制作了说明;

同源策略的含义详解

  • 含义一(DOM层面的同源策略):
    • 限制了来自不同源的”document”对象或js脚本,对当前“document”对象的读取或设置某些属性;
  • 含义二(Cookie和XMLHttprequest层面的同源策略):
    • 只有和本网页同源的脚本才会被执行,有时,AJAX已经加载不同源的脚本,但绝对不会被执行;
  • 含义三(同源策略的非绝对性):
    • 同源策略通常允许进行跨域写操作、通常允许跨域资源嵌入、通常不允许跨域读操作;
    • <script><img><iframe><link><video><audio>等带有src属性的标签可以从不同的域加载和执行资源,同源策略关注的是加载js的页面所在的域,而不是页面内存放的js文件的域;
  • 含义四(其他插件的同源策略):
    • flash、java applet、silverlight、googlegears等浏览器加载的第三方插件也有各自的同源策略,只是这些同源策略不属于浏览器原生的同源策略,如果有漏洞则可能被黑客利用,从而留下XSS攻击的后患;

几个同源策略常见问题

  • 没有同源策略会怎样?为什么同源策略禁止跨域读操作?

    • 设想你打开了一个银行网站,又打开了一个恶意网站,如果没有同源策略,将会:
    • 恶意网站包含了脚本a.js,银行网站在没有加载此脚本的情况下,就可以被此脚本操纵,操纵的后果是:
    • 银行网站页面DOM结构被篡改;
    • 银行网站页面DOM元素的属性和值被篡改;
    • 银行页面发送的表单信息可能被恶意脚本接收到,造成用户名密码泄漏;
    • 恶意网站通过自己加载的恶意js脚本获取了银行网站用户的cookie信息,并将它发送给了银行网站,随后,恶意网站就可以自动的、不受用户限制的、在用户不知情的情况下登录用户的银行网站并且伪装用户发送转账等请求;
  • 有了同源策略会怎样?

    • 浏览器在执行一个js脚本(或其他脚本)前,需要对这个脚本进行同源检测,如果加载这个脚本的页面和当前页面不同源,浏览器将拒绝执行此脚本;
    • 注意,浏览器并不关心js脚本来自何方(不关心js脚本从哪个域名、哪个”源”加载),它只关心加载脚本的那个页面是否和当前页面同源;
  • 为什么<script><img><iframe><link><video><audio>等带有src属性的标签可以不遵守同源策略/为什么同源策略允许跨域嵌入:

    • 现在很多大型网站的js脚本、图片等都不是存放在存储网站页面的那台服务器上,他们很可能通过CDN等方式传送到浏览器端,如果限制他们必须和网站页面同源,无异于自己束缚手脚;
    • 一个网站要加载哪些脚本,由网站的编写人员说了算,他们不会故意加载恶意脚本(比如银行网站的编写人员不会将恶意网站的脚本写在银行网站中),所以只要是写在网页中的脚本,我们认为它是安全的;
    • 所以,a.com的网页中可以写<script src="b.js"></script>,a网站加载了b网站的脚本,这是完全可以的,不受任何限制;
      可以这样认为,只要是页面加载的脚本,都和页面同源,无论这个脚本来自哪个”源”(source);
  • 为什么同源策略允许跨域写操作?

    • 和上面一条理解相同;
    • 比如提交表单这个写操作,表单不一定提交到提供网页页面的网站,很有可能提交到专门处理表单的服务器,如果不允许跨域写,将是很不灵活的;
    • 表单提交到什么地方,是编写页面时程序员决定,程序员不可能故意写一个恶意域名进去,所以写操作通常是安全的;
  • “同源”的具体含义?

    • 网站的同源:域名、协议、端口有一个不同就不是同源,三者均相同,这两个网站才是同源;
    • js脚本的同源:处于同源网站上的js脚本同源,否则不同源;
  • 浏览器沙箱机制:

    • 纵向上,浏览器的不同标签页分属不同的进程,进程间不能相互访问内存;
    • 横向上,浏览器进程、渲染进程、插件进程、扩展进程相互隔离,他们可以相互通信,但要经过严格的安全检测;
    • 浏览器的沙箱机制是从内存管理的角度设计的安全措施,是对同源策略的补充,进一步提升浏览器安全性;

几种跨域访问策略和原理

  • document.domain

    • 浏览器在检测是否同源时肯定要检测域名是否相同,它是通过document.domain属性来获取当前页面域名的;
    • document.domain属性不能随便更改,但可以通过js将document.domain属性设置为当前document.domain属性值的后缀;
    • 例如,假设在 http://store.company.com/dir/other.html中的一个脚本执行了下列语句document.domain = "company.com",这条语句执行之后,浏览器将会成功地通过对http://company.com/dir/page.html的同源检测,但不能设置 document.domain为othercompany.com.
    • 如果需要跨域访问的网站和本网站端口、协议均相同,只有域名不同,而且需要跨域访问的网站的域名是本网站的后缀,则可以使用document.domain暂时更改当前document对象的域名值,实现跨域访问;
    • 此种跨域访问限制颇多,空间上,只能跨域访问协议、端口相同的且域名是本网站后缀的网站,时间上,一旦原网站重新刷新页面,document.domain值恢复原状,不能继续跨域访问,所以这种跨域访问策略只能算是局部的、暂时的、基础域名相同的网站间的跨域访问;
  • window.name

    • 浏览器一个窗口(标签页)的window.name属性在时间上是全局的,无论一个窗口中的页面如何跳转,window.name属性不变;
    • 可以看到,如果在一个标签里面跳转网页的话,我们的 window.name 是不会改变的;
    • 由于安全原因,浏览器始终会保持 window.name 是string类型;
    • window.namedocument.domain更强大,可以从任意页面获取string类型的数据;
  • JSONP

    • JSONP是一种依靠开发人员的聪明才智创造出的一种非官方跨域数据交互协议;
    • JSONP本质是利用了<script><img><iframe>等标签可跨预加载脚本的特性实现数据跨域传输;
    • 本地的js代码将需要请求的数据包装好(如需要某天的某次航班的飞机票数据),发送到远端js,远端js依据本地js提供的信息获取相应的数据传回到本地js;
    • JSONP 的理念就是,我和服务端约定好一个函数名,当我请求文件的时候,服务端返回一段 JavaScript,这段 JavaScript 调用了我们约定好的函数,并且将数据当做参数传入;
  • postMessage

    • postMessage()方法允许来自不同源的脚本(无视协议,端口,域名的不同)采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递;
    • postMessage(data,origin)方法接受两个参数:
    • data:要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果;
    • origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为”*”,这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为”/“;
  • 对于跨域访问的说明:

    • 对于不同的浏览器、不同的业务类型、不同的安全等级、不同的实际情况,对下述内容的管制也不相同,有的允许有的禁止:跨域写、跨域嵌入、跨域读、跨域API接口访问、跨域数据存储(DB)访问、跨域授权情况;
    • 其他的跨域访问机制:CORS(允许一个域上的网络应用向另一个域提交跨域AJAX请求)、OAuth(跨域授权);
    • 严格的同源策略限制所有的跨域读写、跨域访问和跨域资源嵌入,虽然保证了安全性,但是对于大型网站来说,强迫他们把一个网站的所有功能放在一个域名下,降低了效率和网站的可扩展性;
    • 所以现有的同源策略、跨域访问策略是在效率和安全性之间做出的最佳权衡;
您的支持是对我最大的鼓励!