前端跨域方案

2020-02-14

浏览器跨域安全隐患


  • 黑客利用<iframe>嵌入真正的银行登录页面,当用户使用账号密码登录时, js 读取到表单内容,从而窃取账号密码。
  • 用户访问恶意网站时,js 悄悄发送ajax请求到银行登陆页面,利用浏览器自带的用户coockie完成自动登陆,从返回数据中窃取信息。


同源策略 Same Origin Policy


js只能与同一个域(协议+域名+端口相同)中的页面进行通讯。确保了 Cookie、LocalStorage 和 IndexDB 无法读取,DOM对象无法获得,AJAX 请求不能发送。
同源策略是浏览器最基本的安全协议,但同时也给一些合法的跨域需求带来阻碍。


跨域的正确打开方式


1. CORS 跨域资源共享

Cross-origin Resource Sharing

这是一个W3C标准,允许浏览器向服务器发出跨域请求,目前主流浏览器都支持CORS。浏览器将CORS请求分成两类:简单请求、非简单请求。只要同时满足以下两个条件,就属于简单请求

  1. 请求方法是 HEADGETPOST
  2. HTTP的头信息不超出以下几种字段:
  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:application/x-www-form-urlencoded、multipart/form-data、text/plain

简单请求

浏览器直接发出CORS请求,在头信息之中,增加一个Origin字段,表明请求源。
服务端在response header中设置如下字段:

1
2
Access-Control-Allow-Origin: "http://client.com" // 同意接受该域名下的请求
Access-Control-Allow-Credentials: true // 是否允许发送cookie,默认不允许

非简单请求

在正式通信之前,增加一次HTTP查询请求,称为预检请求。浏览器先通过OPTIONS请求询问服务器,当前域是否在服务器白名单中,以及可以使用哪些HTTP动词和头信息字段,得到肯定答复后才发出正式请求。


2. jsonp

利用<script>的src属性来发送请求,在请求的查询字符串中指定接收数据的回调函数,就能将资源下载到本地执行,不受浏览器同源策略约束。缺陷是只能发get请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 原生js方法-动态创建脚本
var script = document.creatElement('script');
script.type = "text/javascript";
script.src = "url?callback=func";
document.body.appendChild(script);

// jquery-ajax方法
$.ajax({
url: "url";
type: 'get',
dataType: 'jsonp',
jsonpCallback: "func",
data: {}
});

// 请求成功后执行的回调函数,res为返回数据
function func(res) {
console.log(res);
}



3. Nginx 代理

Nginx是一款轻量级、高性能的Web服务器,能够实现反向代理、负载均衡
在 nginx.conf 文件中配置代理,将前端域名发送的请求转发到后端域名上

1
2
3
4
5
server {
listen 8080; # 监听前端端口
location / {
proxy_pass http://localhost:8081; # 转发到服务端端口
}

  • 正向代理:为用户做代理,VPN
  • 反向代理:为服务器做代理,实现负载均衡,有效分配流量


4. Web Socket

Web Socket是H5新增的一种在单个tcp连接上建立全双工通信的应用层协议,该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。服务器判断请求中的Origin字端是否在白名单内,来决定是否回应。


5. postMessage

H5新增的window.postMessage()允许跨域、跨文档通信。
主要用于父子页面、多窗口、页面与嵌套的iframe之间的通讯。

A.com/1.html
1
2
3
4
<iframe src="B.com/2.html"/>

// 严格指定接收方,否则信息容易泄漏
$('iframe').contentWindow.postMessage(msg,'B.com/2.html');
B.com/2.html
1
2
3
4
5
6
window.addEventListener('message', function(e) {
// 校验请求源,只接受可信url的信息,否则易遭受XSS攻击
if (e.origin=='A.com/1.html')) {
console.log(e.data);
}
}, false);



6. 主域相同子域不同:document.domin

两个页面强制设置document.domain为基础主域,就实现了同域。
此法只适用于cookie和iframe

http://www.domin.com/a.html
1
2
3
4
<iframe src="http://child.domain.com/b.html"></iframe>

document.domain = 'domain.com';
var user = 'Tom';
http://child.domin.com/b.html
1
2
document.domain = 'domain.com';
console.log(window.parent.user) // Tom