web安全一,同源策略与跨域

之因此要将同源策略与跨域写在一块儿,是由于存在浏览器的同源策略,才会存在跨域问题

何为同源策略

同源策略是浏览器实现的一种安全策略,它限制了不一样源之间的文档和脚本交互的权限。只有同一个源的脚本才会具备操做dom、读写cookie、session 、ajax等敏感操做的权限。能够说同源策略在web安全中扮演着及其重要的角色。所谓同源简单来讲便是两个页面必须具备相同的协议、端口还有域名。

http://www.site.com/index.html为例,举个小栗子:html

url 是否跨域 缘由
http://www.site.com/other/ind...
http://child.site.com/index.html origin 不同(www与child)
http://www.site.com:8090/index.html 端口 不同(80与8090)
https://www.site.com/index.html 协议 不同(http与https)

IE是个例外

不得不调侃一下,在兼容性方面IE彷佛永远都难以跟上别的浏览器发展的步伐,永远都是一个例外。前端

  • 授信范围(Trust Zones):两个相互之间高度互信的域名,如公司域名(corporate domains),不遵照同源策略的限制。
  • 端口:IE未将端口号加入到同源策略的组成部分之中,所以 http://www.size.com:81/index.html 和http://www.size.com:8090/index.html 属于同源而且不受任何限制。

虽然同源策略限制了跨域文档脚本的操做能力,但明确容许部分资源性的标签是能够加载跨域资源的,如<img>,<script>部分具备跨域能力的标签以下,来源于MDNhtml5

  • <script src="..."></script> 标签嵌入跨域脚本。语法错误信息只能在同源脚本中捕捉到。
  • <link rel="stylesheet" href="...">标签嵌入CSS。因为CSS的松散的语法规则,CSS的跨域须要一个设置正确的Content-Type消息头。
  • <img>嵌入图片。
  • <video> 和 <audio>嵌入多媒体资源。
  • <object>, <embed> 和 <applet>的插件。
  • @font-face引入的字体。
  • <frame> 和 <iframe>载入的任何资源。站点可使用X-Frame-Options消息头来阻止这种形式的跨域交互。

跨域的方法

在浏览器同源策略的安全限制下,跨域的需求仍然是有的。这种需求能够分为正当的和不正当的。具体来讲不正当的就是,第三方恶意网站利用同源策略的一些漏洞,对目标网站进行一些不容许的操做,这种操做咱们称之为跨域攻击。具体能够参照个人另外几篇博客:jquery

  • CSRF攻击(跨域伪请求)
  • XSS攻击(跨域脚本攻击)

正当的跨域请求能够是: 一些大集团下面的几个子域名之间的跨域交互,也能够是合做网站之间的跨域交互等等,总之就是被容许的。下面来具体说说实现这种正当跨域的几种方法:git

写在前面: 全部的跨域方法都会带来安全性的问题,一旦恶意网站可以成功在目标网站运行脚本或者具备操做的能力,他们也就具备了目标网站的跨域能力。

document.domain

脚本能够将 document.domain 的值设置为其当前域或其当前域的超级域。github

这种方法的优势就是:简单,只要在前端js中设置就行了
弊端:只适合用于子域与父域之间还有子域之间的跨域web

CORS / Access-Control-Allow-Origin: *

实现起来异常简单,主要是经过在服务器设置HttpHeader,客户端检查本身的域是否在容许列表中,决定是否处理响应。详情请参考CORS MDN, 利用CORS实现跨域请求ajax

服务器端能够在HTTP的响应头中加入(页面层次的控制模式):
Access-Control-Allow-Origin: example.com
Access-Control-Request-Method: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization, Accept, Range, Origin
Access-Control-Expose-Headers: Content-Range
Access-Control-Max-Age: 3600json

多个域名之间用逗号分隔,表示对所示域名提供跨域访问权限。"*"表示容许全部域名的跨域访问。segmentfault

此方法的优势:简单快捷,配置简单,基本能够解决任何域的跨域问题
弊端:仅适用与客户端与跨域服务器的交互;另外具备浏览器的兼容性问题,不兼容IE7以及如下。
在IE8——IE9 中要使用xhr = new XDomainRequest();来替代xhr = new XMLHttpRequest();

查看跨域脚本的错误

在之前,当尝试使用 window.onerror 去记录跨域脚本的错误时,只会返回 Script error, 无法获取到具体的报错信息。这种状况在html5中获得了改观,html新加了一个crossorigin属性,容许捕获跨域脚本的详细错误信息,可是有两个条件:

  1. 跨域脚本的服务器必须经过 Access-Controll-Allow-Origin 头信息容许当前域名能够获取错误信息
  2. 当前域名的 script 标签也必须指明 src 属性指定的地址是支持跨域的地址,也就是要添加crossorigin 属性

crossorigin 具备两个值:
anonymous: 请求不带 credentials flag .
use-credentials: 请求携带 credentials flag .

credentials flag 也就是常说的cookies, client-side SSL certificates

window.postMessage

postMessage是HTML5新的语法特性,可让跨域的脚本之间进行交互,主要分为消息的发送端和消息的接收端。
发送端:
otherWindow是其余窗口的一个引用,好比iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。

otherWindow.postMessage(
message, // 要发送到其余窗体的消息,能够传送任何数据对象
targetOrigin, // 消息要发送到的域,由完整的协议、地址、端口组成
[transfer] // 这是一个可选的抽象接口
);

otherWindow.postMessage() 方法被调用时,会在全部页面脚本执行完毕以后(e.g., 在该方法以后设置的事件、以前设置的timeout 事件,etc.)向目标窗口派发一个 MessageEvent 消息。
接收端:

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
{
  // For Chrome, the origin property is in the event.originalEvent
  // object.
  var origin = event.origin || event.originalEvent.origin; 
  if (origin !== "http://example.org:8080")
    return;

  // ...
}

message 事件,包含4个参数

  • message 属性表示该message 的类型;
  • data 属性为 window.postMessage 的第一个参数即message;

— origin 调用window.postMessage() URI
— source 调用window.postMessage() 的窗口对象

兼容性:在 Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3)以前, 参数 message 必须是一个字符串。而IE10下面也有奇怪的问题

优势:适合同一个浏览器两个跨域页面之间的脚本交互
弊端:只能从窗口的引用(也就是window.open或者window.frames[0].contentWindow)发送消息到目标窗口,适用的范围有限

JSONP

jsonp 是比较古老的跨域方法,也是比较经常使用的跨域
具体来讲,它的实现以下:

function jsonp(url,data,callbackName) {
var script = document.createElement('script');
var src = url+ '?'+ data+ 'calback='+callbackName;
script.src = src;
document.head.appendChild(script);
}

function callbackName(data){
    // data from server;
}

JSONP的坑

由于在jquery中JSONP使用的是ajax的封装接口,不少人它会是ajax的一种技术,实际上它是利用<script>标签天生具备跨域能力的一种技术,因此不少人在jquery中使用JSONP时都会踩坑。

  • 坑一:同步,不少人会想固然地使用ajax的同步参数async来实现同步。实际上jsonp只有异步,由于script是异步加载的
  • 坑二:错误回调,在使用jquery的过程,不少人会使用ajax的errorfail接口,来捕获跨域的异常返回。ajax的错误接口只能捕获请求超时、终止、json解析错误等正常的错误,可是对于网络不通、服务器异常,jquery是选择静默地失败的。

那怎么捕获jsonp的异常呢?
实际上<script>标签在遇到这些错误时会触发error事件。因此上面的代码咱们能够稍微改一下,增长一个捕获异常的回调

function jsonp(url,data,callbackName,errorbackName) {
var script = document.createElement('script');
var src = url+ '?'+ data+ 'calback='+callbackName;
script.src = src;
script.onerror = errorbackName;
document.head.appendChild(script);
}

function callbackName(data){
    // data from server;
}

另外在jquery中的处理主要有两种方法,参考 jQuery使用JSONP时的错误处理

  • 手动暴露jsonp 的错误接口
var head = document.head || $('head')[0] || document.documentElement; // code from jquery
var script = $(head).find('script')[0];
script.onerror(function(evt) {
    alert('error');
});
  • 使用jsonp的拓展库

针对jquery处理jsonp的坑,早有大神封装好插件,直接引用就能够了,方便快捷省心。传送门

【相关】
web安全,是一个很重要的技能,也是一个领域的知识。我把这个领域的东西写成了一个系列,之后还会继续完善下去:
web安全一:同源策略与跨域
web安全二:CSRF 攻击
web安全三:XSS 攻击

相关文章
相关标签/搜索