这篇文章将会解释什么是CSRF,列举一些常见的CSRF漏洞案例,并解释如何避免CSRF攻击。
什么是 CSRF?
CSRF是一个web安全漏洞,攻击者会利用这种漏洞引诱用户进行一些本来不想进行的行为。SOP(same origin policy)是设计出来防止不同网站互相影响的,但是利用这个漏洞,攻击者可以部分绕过它。
CSRF攻击有什么影响?
CSRF攻击一旦完成,攻击者会强迫受害者进行一些行为。例如,修改用户的email地址,修改密码,或者进行资金转移。攻击者甚至可以完全获取对用户账户的控制权。如果该用户在应用产品中享有较高的权限,那么攻击者有可能实现对该应用产品的数据和功能的全面接管。
CSRF是如何攻击的?
CSRF攻击是否成功,有以下三个非常重要的条件:
·一个相对应的行为。这个发生在应用内的行为对攻击者来说存在价值。可能是一个高级特权行为(例如为其他用户修改权限),或者操作一些特殊数据(例如修改用户自己的密码)。
·基于Cookie的会话处理。执行一个行为需要发出一条或者多条HTTP请求,这个应用仅仅依靠会话Cookie来判别发出请求的用户,并且该应用没有其他合适的机制来睿总会话或者验证用户请求。
·请求参数可预知。用户发出的请求包含了攻击者可以确定或者猜测的参数。例如,当用户需要修改密码且攻击者需要知道现有密码的值的时候,这时就不容易受到攻击。
举个栗子,假设有这么一个应用,它允许用户修改自己账户的email地址。那么当用户执行这个行为的时候,用户将会发送这么一个HTTP请求:
POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Cookie: session=yvthwsztyeQkAPzeQ5gHgTvlyxHfsAfE
email=wiener@normal-user.com
那么这就满足了CSRF攻击产生的条件。首先攻击者对修改邮箱地址这一行为是感兴趣的。因为在这个行为之后,攻击者通常能够出发密码重置从而全面接管用户的账户;其次,这个应用采用会话Cookie来判别发送这条请求的用户。没有任何其它的标志或者机制来追踪用户的会话;最后,这一行为的参数值,攻击者可以轻松确定。
在这些条件满足的情况下,攻击者可以构造一个包含以下HTML内容的网页:
<html>
<body>
<form action="https://vulnerable-website.com/email/change" method="POST">
<input type="hidden" name="email" value="pwned@evil-user.net" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
如果用户访问了攻击者设置的页面,那么就会产生一系列的连锁反应:首先攻击者的网页会触发一个发送给原网页的HTTP请求;然后,如果用户已经登录了原网页,浏览器会自动将会话Cookie放入这个请求中;接下来原网页会按照平时那样处理这个请求,从而“帮助”攻击者修改了用户的email地址。
PS:尽管CSRF通常与基于Cookie的会话处理相关,但当应用是自动向用户请求中添加用户凭证时,CSRF攻击也有可能产生,例如HTTP基础认证和基于证书的认证。
如何发起一次CSRF攻击?
通常来说不会手动创建利用CSRF所需的HTML,因为这可能十分麻烦,特别是当请求包含了大量参数或者其它独特的内容时。最简单的方式是使用内置在Burp Suite Professional中的CSRF PoC生成器。(各位道友不要乱来蛤)
如何利用CSRF漏洞?
CSRF攻击的传递机制与反射XSS的传递机制本质上是相同的。通常攻击者会将恶意HTML放置到他们控制的网站上,然后诱使受害者访问该网站。其表现形式通常是通过向用户提供一个网站链接,或者通过电子邮件或社交媒体信息。或者,仅仅将攻击被放置在一个流行的网站(例如,在用户评论中),然后静静等待用户访问该网站。
我们注意到一些简单的CSRF利用了GET方法,在易受攻击的网站上使用一个自包含URL。在这种情况下,攻击者可能不需要使用外部站点,而是可以直接向受害者提供域上的恶意URL。在前面的示例中,如果更改电子邮件地址的请求可以使用GET方法执行,那么自包含攻击将如下所示:
<img src="https://vulnerable-website.com/email/change?email=pwned@evil-user.net">
如何预防CSRF攻击?
最健壮的方式就是在请求中添加CSRF token。这个token应该符合以下特征:极难预测,与用户会话强相关,在执行的时候对进行严格的验证。另一种十分有效的防御措施是SameSite cookies
常见的CSRF漏洞。
大部分CSRF漏洞的产生是由于验证CSRF tokens产生了错误。在之前的样例中,我们这样加入CSRF token:
POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 68
Cookie: session=2yQIDcpia41WrATfjPqvm9tOkDvkMvLm
csrf=WfF1szMUHhiokx9AHFply5L2xAOfjRkE&email=wiener@normal-user.com
这么做理论上可以防止CSRF攻击,因为它破坏了CSRF漏洞产生的某些条件:应用不再仅仅依赖于会话处理的cookie,并且请求中包含了攻击者不能确定的参数。然而,仍然有很多种方式可以破坏掉现有防御,这意味着这个应用仍然不足以防御CSRF攻击。
CSRF token的验证依赖于请求的方式
一些应用能够正确验证POST的请求,但是却跳过了对GET请求的验证。在这种情况下,攻击者会采用GET请求的方式来绕过CSRF token验证,从而发动CSRF攻击:
GET /email/change?email=pwned@evil-user.net HTTP/1.1
Host: vulnerable-website.com
Cookie: session=2yQIDcpia41WrATfjPqvm9tOkDvkMvLm
CSRF token的验证依赖于的token的存在
当token存在的时候,应用能够正确验证,但是当token被忽略的时候,验证就被跳过了。在这种情况下,攻击者可以通过token中的所有参数(不仅仅是参数值)来绕过验证congress发动攻击。
POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 25
Cookie: session=2yQIDcpia41WrATfjPqvm9tOkDvkMvLm
email=pwned@evil-user.net
CSRF token没有与用户会话绑定
一些应用不会去验证与发出请求的用户同属统一会话的token。相反,应用维护了一个全局token pool,这个token pool对于所有处于pool中的token都会直接通过验证。在这种情况下,攻击者可以登录自己的账号,然后获取一个合法的token,然后在CSRF攻击中将token发送给受害用户。
CSRF token 与非会话级cookie绑定
一些应用确实做到了将CSRF token与用户的cookie绑定,但是不是哪些用于追踪会话的cookie,这很容易出现在哪些同时采用多个不同架构的应用中,例如一个架构用于会话处理,一个用于CSRF 防护,但是两者并没有统一,举个栗子:
POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 68
Cookie: session=pSJYSScWKpmC60LpFOAHKixuFuM4uXWF; csrfKey=rZHCnSzEp8dbI6atzagGoSYyqJqTz5dv
csrf=RhV7yQDO0xcq9gLEah2WVbmuFqyOq7tY&email=wiener@normal-user.com
在这种情况下,如果网站存在某种能够使得攻击者在用户浏览器设置cookie的行为,那么就可以进行攻击。攻击者可以用自己的账户登录应用获取一个合法的token,然后绑定一个cookie,然后利用某些可以设置cookie的行为将他们的恶意cookie放入受害者的浏览器中,然后在CSRF攻击中发送token。
ps:设置cookie的行为甚至不需要发生在统一网络应用中。如果攻击者控制的cookie有足够的作用范围,那么在同一个整体DNS域中的其它任何应用都可以用于设置其它目标应用的cookie。例如,例如,攻击者可以利用在staging.demo.normal-website.com
中的cookie设置功能来放置在secure.normal-website.com中用于提交的cookie。
基于Referer的CSRF防御
除了采用CSRF token,一些应用通过采用HTTP Referer header来防御CSRF攻击,它大致是采用了通过验证来自应用域的请求的防御方式。但是这种方法经常会被攻击者绕过。
什么是HTTP Referer header?
HTTP Referer header(该命名来自于HTTP规范中不经意的拼写错误)是HTTP中可选的请求头,它包含了与请求资源相连接的网页URL。它通常是在用户出发HTTP请求的时候由浏览器自动添加的,例如点击一个链接或提交一个表单。有很多种方法允许所链接的网页保留或者修改Referer header的值,不过这通常是处于一些涉及隐私的目的。
对Referer的验证依赖于请求头的存在
一些应用在请求头被忽略的时候会跳过对Referer的验证。在这种情况下攻击者可以通过CSRF攻击使受害用户的浏览器在结果请求中丢弃Referer header。最简单的攻击方式就是在CSRF攻击的HTML页面中使用META标签:
<meta name="referrer" content="never">
对Referer的验证可以被攻击者规避
一些应用采用原生的方法来验证Referer header,而这种验证方式很容易被绕过。例如,如果应用通过验证Referer中所包含的域名是否满足预期,那么攻击者可以把该域名作为自己的子域名:
http://vulnerable-website.com.attacker-website.com/csrf-attack
类似,如果应用仅仅验证Referer是否包含自己的域名,那么攻击者可以把域名放在URL中的其它位置:
http://attacker-website.com/csrf-attack?vulnerable-website.com
ps:也许你可以通过使用Burp(一种基于代理的工具,它可以评估互联网应用的安全性并进行实际测试,它是全世界最广为使用的互联网漏洞屏障),但是当你在浏览器中进行概念验证的测试时,你会发现Burp会失效。为了降低以这种方式泄漏敏感数据的风险,很多浏览器都开始默认禁用在Referer header中进行字符串查询的操作。当然如果你自行设置了unsafe-url的header集合,就可以不采用浏览器的默认设置。