HTTP知识集锦
Cookies是一种最常使用的客户端存储机制,Cookies由Netscape在1994年提出,用来为HTTP协议引入状态机制。
为了使基于Cookies的状态机制能够工作,Web服务器会在HTTP响应的Set-Cookie头中设置Cookie,保存用户身份、状态信息,而浏览器在下一次发送HTTP请求的时候,会把先前Web服务器设置的Cookie绑定在HTTP请求头中,发送回去。
由于每次HTTP请求都会重复的发送Cookies,所有Cookies不适合存放过大的数据,事实上,大部分浏览器对每个域名可设置Cookies的大小、都有严格限制,通常在数十个、几KB以内。
- Cookies的Domain可以设置为IP地址,但是不能使用通配符,例如.0.1这样的Domain是非法的
- Domain:域名至少包含两个点号。域设置为.gmem.cc的Cookie可以让gmem.cc域名下所有网站使用,例如blog.gmem.cc
- Path:限制使用Cookie的网页目录,路径设置为/wp-admin/的Cookie可以让gmem.cc/wp-admin/下所有子URL对应的网页访问
- Secure:可以限制Cookie只能通过加密连接(SSL)发送
- Expires:可以限制Cookie的生存期,如果不设置,那么浏览器关闭后Cookie即消失
所谓源,是指由三元组:协议 + 域名 + 端口确定的唯一网络实体。
通过JavaScript可以修改当前页面的源,但是只能域名为当前域名的超级域名:
1 |
document.domain = "gmem.cc"; |
修改后,同源策略检查结果将受到影响。使用这种技术,可以让位于子域名中的多个网站方便的交互。
原则:
- 通常允许跨域执行写操作:例如链接、重定向、表单提交
- 通常允许跨域资源嵌入
- 通常不允许跨域读操作:例如Ajax请求
其中第一条,为CSRF(跨站请求伪造,Cross-site request forgery)埋下隐患。攻击者可能引导用户访问一个恶意站点,而此恶意站点包含了修改敏感信息的表单,此表单自动跨源发送,伪造用户身份篡改信息。可以使用不可测的CSRF标记来防止跨域写。
第二条,可以嵌入的资源包括:
- script标签
- link标签,嵌入css。CSS的跨域需要一个设置正确的Content-Type消息头
- img标签
- video、audio标签
- object、embed、applet插件
- @font-face引入的字体。某些浏览器要求同源字体
- frame、iframe。站点可以使用 X-Frame-Options 头禁止这类资源嵌入
你可以使用CORS、JsonP等技术允许来进行跨源的资源共享。
JsonP即JSON with Padding,其实与JSON没有直接关系,是一种跨站资源共享的方式。
由于浏览器的同源策略,blog.gmem.cc一般是无法与诸如soulspark.im之类的其它网站进行通信的,但是HTTP的 <script> 脚本是一个例外,例如此脚本,可以动态的从其它网站上获取信息。
假设blog.gmem.cc/postinfo/{postid}提供一个获取文章信息的RESTful服务,其返回值是text/json格式,示例如下:
1 2 3 4 |
{ "post_author" : "alex", "post_title" : "HTTP协议知识集锦" } |
那么soulspark.im的某个页面如何才能访问并获取某篇文章的信息呢?
如果直接使用Ajax请求访问,同源策略可能导致访问被禁止,那么如果使用 <script> 脚本,上述返回值并不能被soulspark.im的脚本提取并使用。
要支持JsonP,blog.gmem.cc必须改造postinfo服务,允许其传入一个“Padding”参数,该参数由调用者soulspark.im指定,通常是一个JavaScript函数的名称,改造后的postinfo服务的URL示例为: blog.gmem.cc/postinfo/{postid}?jsonp=decodePost ,而返回值则是如下形式的脚本:
1 2 3 4 5 6 7 |
//decodePost即所谓Padding,由客户端提供的简短字符串,通常就是所谓“回调函数” decodePost( { "post_author" : "alex", "post_title" : "HTTP协议知识集锦" } ); |
这样,soulspark.im就可以调用decodePost(),从而获取文章的信息了。
需要注意的时, <script> 脚本不一定非要硬编码在HTML文档中,在页面运行过程中,可以随时动态创建该元素以使用JsonP。
JsonP允许远端Web服务注入任意JavaScript代码,因此具有很大的安全性风险,如果远端服务器具有安全缺陷,或者响应内容在网络上被篡改,将导致本地Web页面受到威胁。
CORS,即Cross-origin resource sharing(跨来源资源共享)是一份浏览器技术规范。相比起JsonP,它有如下优势:
- 支持除了GET以外的其它HTTP请求方法
- 支持使用一般的Ajax(XMLHttpRequest、Fetch)直接获取资源
- 支持跨源使用Webfont、WebGL贴图、样式表、脚本
所谓跨源,指发起请求的那个网页所在的源(协议 + 域名 + 端口),与请求的目标网页的源不一致。
大部分现代浏览器,包括IE 8+,均支持CORS。
要支持CORS,服务器端必须设置 Access-Control-Allow-Origin 响应头,下面是某个Python Web服务的代码片段:
1 2 3 4 5 |
headers.setdefault('Access-Control-Allow-Origin', '*') """ 上述配置允许任何其它的Domain向服务器发起请求。 把*换成http://soulspark.im,则仅仅允许soulspark.im发起请求 """ |
如果服务器不进行设置,客户端在尝试CORS访问时将会报错。
与CORS有关的响应头包括:
Access-Control-Allow-Origin | 必须字段。允许哪些源发起请求,源由客户端在请求头 Origin 中设置 |
Access-Control-Allow-Credentials | 布尔值,可选字段。是否允许跨域的发送Cookie,默认情况下Cookie不包含在CORS请求中 |
Access-Control-Expose-Headers | 默认的,CORS方式发送Ajax时,只能拿到Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma这几个响应头。如果允许获得其它响应头,需要在这里指定 |
在浏览器中,Origin头会在需要时,自动的被浏览器设置,JavaScript代码无法干预。因此CORS依赖浏览器的正确行为来工作。
发送CORS的Ajax请求时要想带着Cookie,不但需要服务器同意,客户端也要显式的指定:
1 2 |
var xhr = new XMLHttpRequest(); xhr.withCredentials = true; // 附带Cookie |
启用上述设置时,Access-Control-Allow-Origin不允许指定为*,同时Cookie仍然受到同源策略的限制。
由于CORS依赖于浏览器的行为,因此服务器不能对客户端做假设。
Leave a Reply