0x00 前言
浅浅地窥探下前端安全
0x01 同源策略
同源策略是浏览器的一个安全功能,只有在同协议、同域名、同端口的情况下浏览器才能接受资源。比如针对 http://www.a.com/
来说
- http://www.a.com/dir/file.html 同源
http://abc.www.a.com/dir/file.html 不同源(域名不同)- file://www.a.com/dir/file.html 不同源(协议不同)
- http://www.b.com/dir/file.html 不同源(域名不同)
- http://www.a.com:8080/dir/file.html 不同源(端口不同)
0x02 Jsonp跨域
JSONP 是 JSON with padding
(填充式 JSON 或参数式 JSON)的简写。
原理
假如在a.com
的jsonp.html
中创建一个回调函数p(),动态添加<script>
标签元素,利用src属性向服务器发送请求,请求地址后面加上查询字符串,通过callback
参数指定回调函数的名字,请求地址为http://b.com/jsonp.js?callback=p
。服务器调用这个回调函数p()
,然后以json数据形式作为参数传递,完成回调跨域。
实例
www.a.com
下的客户端jsonp.html
:
1 |
|
www.yoga.com
服务器端json.php
1 |
|
结果
特点
- 优点
它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。
- 缺点
它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
Json Hijacking
属于读取型的CSRF,当某网站听过 JSONP 的方式来跨域(一般为子域)传递用户认证后的敏感信息时,攻击者可以构造恶意的 JSONP 调用页面,诱导被攻击者访问来达到截取用户敏感信息的目的。
原因
- Referer过滤不严格
- 空Referer绕过
- 随机不安全的Token
Tips
假如响应报文中的Content-Type
为text/plain
,是无法进行JSONP 劫持的,表示的是纯文本,浏览器会在返回结果前后加上<pre></pre>
标签,这样所获得的数据就不再是纯JS内容了,也就没办法用script去引用然后劫持内容
实例
受害者服务器端:json.php
1 |
|
钓鱼页面:jsonp.html
1 |
|
攻击者VPS:jsonp.php
1 |
|
受害者打开钓鱼页面时jsonp.html
,就会远程调用存在漏洞的页面json.php
,然后执行回调函数发送一个XHR请求将获取的数据发送至攻击者的VPS的jsonp.php
上,成功将受害者敏感信息记录下来。
修复方案:
- Referer正则匹配
- 添加Token
- 放弃使用Jsonp跨域获取数据,使用CORS或Post Message
0x03 CORS跨域
CORS是一个W3C标准,全称是”跨域资源共享”,它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。但是实际上,AJAX在发送跨域请求时,请求是能够正常发出去的,而且服务端也是可以正常接收并能做出响应,只是浏览器拒绝接收跨域服务器返回的数据。
CORS本质上就是使用各种头信息,浏览器与服务器之间进行身份认证实现跨域数据共享
在CORS中,所有的跨域请求被分成了两种类型
- 简单请求
- 复杂请求
它们的区分原则:满足所有以下所有的条件(缺一不可
)就属于简单请求,否则就是复杂请求。
- 请求方法必须是 GET、HEAD、POST中的一种,其他方法不行;
- 请求头类型只能是
Accept、Accept-Language、Content-Language、Content-Type
,添加其他额外请求头不行; - 请求头 Content-Type 如果有,值只能是
text/plain、multipart/form-data、application/x-www-form-urlencoded
中的一种,其他值不行; - 请求中的任意
XMLHttpRequestUpload
对象均没有注册任何事件监听器; - 请求中没有使用
ReadableStream
对象。
简单请求
实质上与普通的AJAX请求类似,当浏览器识别出是一个跨域请求的时候,就自动在Request头部增加一个Origin字段(用户不可控),告知服务器是从哪个源发起的。假如服务器允许本次请求响应的数据可以共享,那么就会在响应头加一个Access-Control-Allow-Origin
等控制字段,然后浏览器会通过响应头的这些字段来决定是否接收返回的数据。
此处,响应的控制字段大概有这么几种:
- Access-Control-Allow-Origin: 允许跨域访问的域,可以是一个域的列表,也可以是通配符”*”。这里要注意Origin规则只对域名有效,并不会对子目录有效。
- Access-Control-Allow-Credentials: 是否允许请求带有验证信息(Cookies),是一个布尔值
- Access-Control-Expose-Headers: 允许脚本访问的返回头,请求成功后,脚本可以在XMLHttpRequest中访问这些头的信息
- Access-Control-Max-Age: 缓存此次请求的秒数。在这个时间范围内,所有同类型的请求都将不再发送预检请求而是直接使用此次返回的头作为判断依据,非常有用,大幅优化请求次数
- Access-Control-Allow-Methods: 允许使用的请求方法,以逗号隔开
- Access-Control-Allow-Headers: 允许自定义的头部,以逗号隔开,大小写不敏感
请求的控制字段:
- Access-Control-Request-Headers:用于发起一个预请求,告知服务器正式请求会使用那些 HTTP 头
- Access-Control-Request-Method: 用于发起一个预请求,告知服务器正式请求会使用哪一种 HTTP 请求方法。
- Origin: 指示获取资源的请求是从什么域发起的
其中,这个ACAC头部得注意下,一般而言,对于跨域请求,浏览器不会发送身份凭证信息(Cookies)。如果要发送凭证信息,必须在ajax请求中设置withCredentials
属性,否则即使服务器同意发送Cookie,浏览器也不会发送。
1 | xhr.withCredentials = true; |
与此同时,ACAO头也不能够为 *
,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie
也无法读取服务器域名下的Cookie。
复杂请求
源测试代码加了一个自定义的头部,不满足简单请求的条件,故该请求为复杂请求
与简单请求不同的是,复杂请求要求首先必须使用OPTIONS方法
发起一个预检请求到服务器,以取得服务器的确认,如果确认成功了,就会再发送一个正常的请求包,然后浏览器会接收返回的数据。
由于服务器端未设置header(“Access-Control-Allow-Headers:lalala”);,于是会报错:
已拦截跨源请求:同源策略禁止读取位于 http://www.yoga.com/ajax2/formdata.php 的远程资源。(原因:来自 CORS 预检通道的 CORS 头 ‘Access-Control-Allow-Headers’ 的令牌 ‘lalala’ 无效)
这时将头部添上,重新发起请求,结果就十分明了了
CORS跨域资源共享漏洞
原理
配置不当,Origin源未严格,从而造成跨域问题。
实例
服务器端 login.php
(模拟登陆用户成功,设置cookie)
1 |
|
服务器端 secrect.php
(配置跨域头)
1 |
|
攻击方: steal.html
1 |
|
攻击方:save.php
(保存用户信息)
1 |
|
首先将配置头信息进行注释,访问login.php
产生cookie 再访问screct.html
,最后访问恶意链接 steal.html
浏览器无法渲染 但是数据是成功返回了
此时只打开ACAO
头部,依旧报错,确少ACAC
头,同样数据也是正常返回了,只是浏览器不解析
最后打开ACAC
头 重复操作
无报错 并且生成了secrect.html
文件
利用条件
“Access-Control-Allow-Origin” | “Access-Control-Allow-Credentials” | Exploitable |
---|---|---|
https://a.com | true | Yes |
null | true | Yes |
* | true | No |