【常见安全测试项目总结-1】这篇文章中,有提到通过CSRF攻击伪造用户请求。在学习的时候有想到既然有浏览器的同源策略,为什么还会有CSRF攻击呢?在我的记忆中,浏览器同源策略会阻止向非同源地址发送AJAX请求,在这种情况下,恶意网站伪造的用户请求是无法发送到目标站点的呀?经过进一步学习后发现:在同源策略下,浏览器发现跨域请求时,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。看到这里发现自己对于跨域问题的理解还是有些肤浅,故又看了些文章写下了这篇笔记。

什么是同源策略

同源

同源指目标的协议域名子域名端口 均为一致,即为同源。

限制

同源策略对于非同源、跨域的请求设置了各种限制。这种限制并不能简单的理解为禁止获取非同源的资源(<img>,<link>,<script>标签都可以请求到非同源内容),也不能理解为禁止跨域请求(因为AJAX确实发出去了,只是服务端的响应被屏蔽了,我就是掉进了这个误区中)。准确的说,同源策略的限制如下:

限制获取cookies、LocalStorage等存储

限制DOM节点

限制AJAX的请求结果获取

实例使用时,我遇到的较为常见的触发这三条限制的场景为:

  • 通过AJAX调用跨域API
  • 在iframe中试图修改外界DOM

跨域可用方案

那么当我们的需求遇到了同源策略的限制,应该如何解决呢?这里转述了下大佬总结的一些跨域解决方案:

JSONP

JSONP的原理是利用<script>标签没有同源限制的特性,通过<script>标签获取服务端动态生成的JSON数据。举例如下:

后端将返回的JSON作为参数放入一个与前端约定好的回调方法中。如约定回调方法名为callback,则后端返回以下内容:

1
"callback("+JSONStr+")"

前端则通过向页面写入<script>标签的方式来向后端地址发送get请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function jsonp({ url, params, callback }) {
return new Promise((resolve, reject) => {
let script = document.createElement('script')
window[callback] = function(data) {
resolve(data)
document.body.removeChild(script)
}
params = { ...params, callback } // wd=b&callback=show
let arrs = []
for (let key in params) {
arrs.push(`${key}=${params[key]}`)
}
script.src = `${url}?${arrs.join('&')}`
document.body.appendChild(script)
})
}

jsonp({
url: 'http://localhost:3000/say',
params: { wd: 'Iloveyou' },
callback: 'callback'
}).then(data => {
console.log(data)
})

本质上是向页面中引入了callback(JSONStr)这句函数调用,调用我们在前端中预先定义好的callback函数,而数据作为参数传入了前端。

以上的调用方式较为麻烦,jQuery则为我们提供了封装好的JSONP调用方法:

1
2
3
4
5
6
7
8
9
$.ajax({
url:"http://crossdomain.com/jsonServerResponse",
dataType:"jsonp",
type:"get",
jsonpCallback:"show",
jsonp:"callback",
success:function (data){
console.log(data);}
});

CORS

CORS需要浏览器与后端共同支持。浏览器会自动进行CORS通信,而后端则需要配置。

后端需要通过设置Access-Control-Allow-Origin来开启CORS。这个属性用于设置那些域名可以访问该资源。

前端在通过CORS发送跨域请求时,分为简单请求与发杂请求两种情况。简单请求指方法为GET / post / HEAD之一,且Content-Type为test/plain / multipart/form-data / application/x-www-form-urlencoded之一。其余的请求为复杂请求,复杂请求在通信前会增加一起http请求,来确定服务端是否允许跨域请求。

反向代理

既然我们知道同源策略限制的是非同源的请求,那么只要我们通过Nginx等支持反向代理的服务器将我们需要的地址代理到同一域名端口下,非同源请求自然就变成了同源请求。

我们需要做的是反向代理目标接口,并且修改cookie中的domain信息,以供当前域写入cookie,实现跨域带cookie请求。示例Nginx配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// proxy服务器
server {
listen 81;
server_name www.domain1.com;
location / {
proxy_pass http://www.domain2.com:8080; #反向代理
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
index index.html index.htm;

# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.domain1.com;
#当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}

以上三种是最主流的解决跨域访问的方案了,其余还有一些方法,个人觉得不是很常用,感兴趣的朋友可以从文末的引用中看下大佬的原文。

References

https://juejin.im/post/6844903767226351623

https://segmentfault.com/a/1190000020962174