程序员应对浏览器同源策略的姿势
发布于 2 个月前 作者 yan 158 次浏览

同源策略

浏览器最基本的安全规范——同源策略(Same-Origin Policy)。所谓同源是指域名、协议、端口相同。 不同源的浏览器脚本(javascript、ActionScript、canvas)在没明确授权的情况下,不能读写对方的资源

同源策略规定了浏览器脚本互操作web数据的基本原则,若没有这一基本原则,那么:

  • 某域下DOM元素被另一方任意操作、篡改,导致页面显示失控* 某域下的cookie等与该域相关的数据片段可以随意读取,导致与该域密切相关的浏览器cookie片段可能失真* 恶意网站能随意执行Ajax脚本偷取隐私数据,导致该域下核心业务数据被抓取。## 同源策略在实施中面临的问题

默认的同源策略 限制了脚本互操作其他域的能力,大棒一挥, 关闭了A站脚本正常访问B站数据的需求。有以下变通方法:

  • 实现CORS (Cross-Origin Resource Sharing)* 使用JSONP (JSON Padding)* 建立一个本地代理服务器,这样先同源访问,由代理服务器转发请求 “ 方案1:CORS是w3C对于跨域请求推出的明确方案;方案2、3均是Hack行为。

CORS跨域请求方案

W3C推出的跨域请求方案: 让web服务器明确授权非同源页面脚本来访问自身,以Response特定标头Access-Control--体现;目前现代浏览器均认可并支持这些标头* 。 CORS特定HTTP标头,为浏览器提供了授权脚本跨域访问其他域名页面数据的通道。

常规的带Cookie Ajax跨域请求

const invocation = new XMLHttpRequest(); 
 
const url = 'http://bar.other/resources/credentialed-content/'; 
 
function callOtherDomain(){ 
 
 if(invocation) { 
 
   invocation.open('GET', url, true); 
 
   invocation.withCredentials = true;   // Ajax请求默认不会发送凭据, 这里设定在Ajax跨域请求中发送凭据 
 
   invocation.onreadystatechange = handler; 
 
   invocation.send();  
 
 } 
 
} 

CORS规范

  • 浏览器发起CORS或POST请求,浏览器会自动携带Origin标头(指示请求来自于哪个站点)* Web服务器实现跨域访问授权逻辑, 授权结果在Response中以Access-Control–*** 标头体现 “ 最常见的Access-Control-Allow-Origin标头包含  * / Origin /null三种响应值;当请求是携带凭据的跨域请求,不可囫囵吞枣地指定为*通配符,而必须指定特定Origin
  • 浏览器会遵守Access-Control–***-- 标头值所施加的跨域限制
GET /resources/access-control-with-credentials/ HTTP/1.1 
 
Host: bar.other 
 
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre 
 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
 
Accept-Language: en-us,en;q=0.5 
 
Accept-Encoding: gzip,deflate 
 
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 
 
Connection: keep-alive 
 
Referer: http://foo.example/examples/credential.html 
 
Origin: http://foo.example 
 
Cookie: pageAccess=2 
 


 


 
HTTP/1.1 200 OK 
 
Date: Mon, 01 Dec 2008 01:34:52 GMT 
 
Server: Apache/2.0.61 (Unix) PHP/4.4.7 mod_ssl/2.0.61 OpenSSL/0.9.7e mod_fastcgi/2.4.2 DAV/2 SVN/1.4.2 
 
X-Powered-By: PHP/5.2.6 
 
Access-Control-Allow-Origin: http://foo.example 
 
Access-Control-Allow-Credentials: true 
 
Cache-Control: no-cache 
 
Pragma: no-cache 
 
Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT 
 
Vary: Accept-Encoding, Origin 
 
Content-Encoding: gzip 
 
Content-Length: 106 
 
Keep-Alive: timeout=2, max=100 
 
Connection: Keep-Alive 
 
Content-Type: text/plain 
 


 


 
[text/plain payload] 

以上表示了一个常见的携带cookie跨域Ajax Get请求,其中Access-Control-Allow-Credentials: true指示浏览器可以将跨域请求的Response结果暴露给页面。

预检Preflight

对于非简单Ajax请求(通常是GET以外的HTTP方法,或者某些MIME类型的POST用法),CORS规范要求发起"预检"请求。

“ 不过,预检请求不需要你手动发起,浏览器会自动使用OPTIONS请求方法从服务器请求支持的方法,然后在服务器“批准”时,使用实际的HTTP请求方法发送实际请求。

下图显示 浏览器判断 非简单请求的逻辑图:

下面使用POST动作发起Ajax跨域请求,同时自定义request header:X-PINGOTHER,该请求触发浏览器预检行为

const invocation = new XMLHttpRequest(); 
 
const url = 'http://bar.other/resources/post-here/'; 
 
const body = '<?xml version="1.0"?><person><name>Arun</name></person>'; 
 
    
 
function callOtherDomain(){ 
 
 if(invocation) 
 
   { 
 
     invocation.open('POST', url, true); 
 
     invocation.setRequestHeader('X-PINGOTHER', 'pingpong'); 
 
     invocation.setRequestHeader('Content-Type', 'application/xml'); 
 
     invocation.onreadystatechange = handler; 
 
     invocation.send(body);  
 
   } 
 
} 

程序员调试CORS的苦恼

跨域请求发生在A—>B 两站,作为某一方开发人员,调试CORS相对麻烦。

经过本StackOverFow工程师的检索,curl 工具可优雅高效模仿Ajax跨域请求:

#  http://example.com 向谷歌站点发起一个跨域Get请求 
 
curl -H "Origin: http://example.com" --verbose \ https://www.googleapis.com/discovery/v1/apis?fields= 

从浏览器Network,将请求以cUrl格式拷贝出来,改改。

总结

● 修复搜狗、360等浏览器不识别SameSite=None 引起的单点登录故障

●  MongoDB副本集自动故障转移机制、客户端监控

● HTTP Strict Transport Security (HSTS) in ASP.NET Core

● 解读docker swarm能力,实战演练Docker Swarm集群编排

● 全网最深刻的ASP.NET Core跨平台技术内幕

● AspNetCore结合Redis实践消息队列

● TPL Dataflow组件应对高并发,低延迟要求

转载是一种动力,分享是一种美德 

如果你觉得文章还不赖,您的鼓励是 原创干货 作者 的最大动力,让我们一起激浊扬清。

长按二维码

关注我们

回到顶部