文章目录
  1. AJAX
  2. XMLHttpRequest
  3. 同源政策
    1. COOKIE
    2. iframe
    3. AJAX
      1. JSONP
      2. WebSocket
      3. CORS
  4. CORS 跨域资源共享
    1. CORS的两种请求
      1. 简单请求
      2. 非简单请求
        1. 1.预检
        2. 2.预检的回应
        3. 3.浏览器的正常相应和回应
  5. 后记

XSS的关于同源的知识

在学XSS前一直没有弄得很明白同源,AJAX,XMLHttpRequest,看文章时又遇到CORS,一团乱。这两天学了一下,做个总结。

AJAX

AJAX是一种用于快速创建动态网页的技术,他可以不需要重新加载网页,就对网页中的一部分进行更新。而XMLHttpRequest是AJAX的基础。

XMLHttpRequest

XMLHttpRequest 是一个浏览器的接口,他让javascript可以进行HTTP或者HTTPS通信。微软在IE5中引用这个接口,可能由于太TM好用了,别人家的浏览器都纷纷使用,于是ajax操作就诞生了。

同源政策

当大致了解AJAX和XMLHttpRequest后就可以了解同源政策了。
1995年,Netscape将同源政策引入浏览器,现在所有浏览器都用这个政策。这个政策最初的含义是:A设置的Cookie,B不能打开。不然就不安全了。但是除了“同源”的:协议相同,域名相同,端口相同。

随着互联网发展,同源政策越来越严格了,现在以下行为受到限制:

  1. Cookie、LocalStortage、和IndexDB无法读取
  2. DOM无法获得
  3. AJAX请求不能发送

如果二级域名不相同的话,浏览器允许通过设置document.domin共享cookie: document.domain='a.com';
接着A网页设置一个document.cookie='aaaa'; B网页就可以读到这个cookie
这只适用于Cookie和iframe窗口,LocalStorage和IndexDB无法使用这种方法。
另外,服务器可以在设置cookie设置为一级域名:.a.com,这样二三级的域名都可以读这个cookie啦!

iframe

如果不同源的话,在一个网页中的iframe是无法读取dom的,父窗口读子窗口的dom还是子窗口读父窗口的dom都会因为跨源而报错。
但如果两个窗口一级域名相同,就可以使用设置document.domin属性,拿到dom。
对于完全不同源的网站,有三种方法可以绕过跨源通信问题:

  1. 片段识别符
    就是指的URL的#后面的部分。比如 www.a.com/aa.html#aaa 中的 #aaa。 如果只是改变片段识别符,页面不会刷新。
    父窗口可以把信息写到子窗口的片段标识符,子窗口通过hashchange事件得到通知。
    同样子窗口也可以改变父窗口的片段标识符。
  2. Window.name
    浏览器有window.name属性,这个属性的特点是无论同源,只要在一个窗口里,前一个网页设置这个属性,后一个网页就可以读取他。
    做法如下:父窗口打开一个子窗口,载入一个非同源网页,将网页信息写入window.name属性中:window.name=data;
    接着,子窗口跳回一个主窗口同源的网址 location='www.a.com'
    主窗口就可以读取子窗口的window.name了~
    优点是window.name容量大,可以放很多信息。缺点是必须监听子窗口window.name变化,影响性能.
  3. 上面的两个都是破解方法,官方解决方案是:window.postMessage
    HTML为了解决跨域通信,引入了全新的API,这个API为window对象新增了一个window.postMessage方法,允许跨窗口通信。

AJAX

同源政策规定,AJAX不能跨源,不然就报错。
除了架设代理服务器,还有三种方法规避

  1. JSONP
  2. WebSocket
  3. CORS

JSONP

他的基本思想是,添加一个<script>元素,向服务器请求JSON数据,将请求到的数据放在一个指定的回调函数中传回来。
具体内容: JSONP菜鸟教程

WebSocket

这是一种通信协议,使用ws(非加密)或wss(加密)作为协议前缀。该协议不实行政策,只要服务器支持就可以跨源通信。
使用WebSocket协议时,有一个http header是origin,指的是请求的发起域名。服务器根据白名单判断是否可通过通信,如果在白名单,就做出回应。

CORS

是跨源资源分享的缩写,他是W3C的标准,是跨源AJAX的根本解决方法。相比JSONP只能发起GET请求,CORS允许类型的请求。

CORS 跨域资源共享

CORS是W3C标准,目前所有浏览器都支持此功能,IE浏览器不能低于IE10。

CORS通信由浏览器自动完成,不需要用户参与。在CORS通信中关键在于服务器,只要服务器实现CORS通信就可以跨域通信。

CORS的两种请求

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
只要同时满足以下两大条件,就属于简单请求。

1
2
3
4
5
6
7
8
9
10
1. 请求方法是以下三种方法之一:
HEAD
GET
POST
2. HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

简单请求

简单的请求的流程如下:

浏览器发出一个请求,在头信息中增加Origin字段,用来说明本次请求来自哪个源,服务器根据这个值决定是否同意请求。
如果服务器不同意,返回一个正常HTTP回应,但是这个回应不包含Access-Control-Allow-Origin字段。浏览器没有发现这个字段,就知道出错了,被XMLHttpRequestonerror回调函数捕获。
如果服务器同意,则返回相应,并带着以下几个头字段。

1
2
3
4
Access-Control-Allow-Origin: http://a.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

Access-Control-Allow-Origin 表示接受的域名的请求。如果是*则表示接受任何域名请求。
Access-Control-Allow-Credentials 表示是否允许发送Cookie,是为true,如果不是不需要为false,只需要删除这一字段名称即可。
Access-Control-Expose-Headers 该字段可选。这个字段表示XMLHttpRequest对象的getResponseHeader()可以拿到除了Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma头以外的写入该字段的名称的头部。

非简单请求

非简单请求的流程如下:

1.预检

浏览器判断一个请求不是简单请求,就会选择非简单请求,并在在正式通信前进行一个一个“预检”。这是一个预检的请求HTTP头部信息:

1
2
3
4
5
6
7
8
OPTIONS /cors HTTP/1.1
Origin: http://a.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

预检使用的请求方法是options,其中Origin表示请求来自哪个源,除了它,还有两个特殊的头部:Access-Control-Request-MethodAccess-Control-Request-Headers

Access-Control-Request-Method 用来列出浏览器的CORS可能会用到哪些HTTP方法。
Access-Control-Request-Headers 字段用逗号分隔字符串,指定浏览器CORS请求会额外发送哪些头部信息,这个例子中是X-Custom-Header

2.预检的回应

服务端检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers后,确认请求,就会发出回应。

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://a.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

在相应头部信息中:
Access-Control-Allow-Origin 表示允许跨域通信的域名。如果是*表示允许任意域名跨域通信。
Access-Control-Allow-Methods 是必须的字段,他用逗号分隔字符串,表示浏览器支持的所有跨域请求的方法。
Access-Control-Allow-Headers 如果浏览器请求使用了Access-Control-Request-Headers字段,则这个字段是必须的,也是用逗号分隔字符串,表示服务器支持的所有头部字段。
Access-Control-Allow-Credentials 和简单请求的字段含义相同、
Access-Control-Max-Age 表示本次预检有效期。

如果服务端没有通过预检,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。控制台会打印出如下的报错信息。

1
2
XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.

3.浏览器的正常相应和回应

一旦服务端通过了预检,则每次浏览器都进行正常CORS请求,就跟简单请求一样。每次相应都有一个Origin头部信息,服务器的回应,也都会有一个Access-Control-Allow-Origin字段。这是一个预检后的正常CORS通信请求

1
2
3
4
5
6
7
PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

如下是服务端的回应

1
2
Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8

后记

学习目的所限,知识的边界并不宽,对于细致的内容这里并没有深入研究。本文也并没有将学习的文章中所有的知识点写出,但对于跨域通信,CORS,已经有了大致的总结,在以后的学习中也必然会深入的研究。

1
2
3
4
5
6
7
8
学习文章:
http://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.html
http://www.runoob.com/json/json-jsonp.html
http://www.runoob.com/json/json-tutorial.html
http://www.cnblogs.com/zknublx/p/5850788.html
http://www.ruanyifeng.com/blog/2016/04/cors.html
http://www.w3school.com.cn/ajax/index.asp
http://www.w3school.com.cn/xml/xml_http.asp
支持一下
扫一扫,支持forsigner