跨站请求伪造(也称CSRF,Cross-site request forgery)是一种web安全漏洞,该漏洞允许攻击者诱导用户做他们本不想做的事情。它使得攻击者可以部分地绕过同源策略——该策略可以阻止不同的web站点进行交互。
如果CSRF攻击成功,攻击者可使受害者用户无意中执行了一些操作,比如修改账号绑定的邮箱,修改密码或者进行转账。根据操作的性质,攻击者可能会完全控制用户的帐户。 如果受感染的用户在应用程序中具有特权角色,则攻击者可能能够完全控制所有应用程序的数据和功能。
CSRF攻击要想成功,必须具备三个必要条件:
举个例子,假设某个应用程序包含一个用户修改账户邮箱的接口,当用户执行这个操作时,会发出一个类似于下面这样的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的三个必备条件:
有了这上面的几个条件,攻击者可以建一个web页面,里面放下面这个表单:
<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>
如果受害者访问了这个页面,下面的几个事情会发生:
注意:尽管CSRF通常被认为是和基于Cookie的会话处理相关,但是它也会发生在那些会自动将用户凭证添加到请求中的场景生效。
手动创建CSRF攻击所需的HTML可能很麻烦,尤其是当涉及的参数比较多的时候,或者涉及的请求有些别的怪异之处的时候。最简单的方式是使用Burp Suite专业版中内置的CSRF PoC(proof-of-concept)生成工具。
跨站点请求伪造攻击的利用机制与反射型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 cookie是对CSRF部分有效的另一种防御方法,可以与CSRF token结合使用。
最有趣的CSRF漏洞是由CSRF token验证中的错误引起的。
在前面的示例中,假设应用程序现在在更改用户密码的请求中包含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的攻击。
有些应用仅在请求方法是POST的时候进行CSRF token的校验,在请求方法是GET的时候则不校验。这在该接口只支持POST方法时是没有问题的。但是有的应用的相关接口既支持POST,也支持GET,那么这种场景下,只在POST方法进行校验就有问题了,因为攻击者可以通过GET方法发起攻击:
GET /email/change?email=pwned@evil-user.net HTTP/1.1
Host: vulnerable-website.com
Cookie: session=2yQIDcpia41WrATfjPqvm9tOkDvkMvLm
有些应用程序只在接口提交了CSRF token时才校验CSRF token有效性,如果接口没有携带CSRF token则略过。这种情况下攻击者可以故意不携带CSRF token参数发起攻击:
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
某些应用程序不会验证token与发出请求的用户属于同一会话。 而是,应用程序维护已发出的全局token池,并接受该池中的所有token。
在这种情况下,攻击者可以使用自己的帐户登录到应用程序,获取有效token,然后在其CSRF攻击中将该token提供给受害用户。
在上述漏洞的一种变体中,某些应用程序确实将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和该token相关的cookie,利用cookie的设置行为将其这个token相关的cookie置于受害者的浏览器中,并在CSRF攻击中将其token提供给受害者。
注意:cookie设置行为甚至不必与CSRF漏洞存在于同一Web应用程序中。 如果所控制的Cookie具有适当的domain范围,则可以利用同一主DNS域中的任何其他应用程序在目标应用程序中设置Cookie。 例如,可以利用staging.demo.normal-website.com上的cookie设置功能来放置提交到secure.normal-website.com的cookie。
在上述漏洞的另一种变体中,某些应用程序不维护任何已发布token的服务器端记录,而是复制cookie中的token到每个request的参数中。 当后续请求进行验证时,应用程序仅验证请求参数中提交的token是否与cookie中提交的值匹配。 有时将其称为针对CSRF的“双重提交”防御,之所以被提倡,是因为它易于实现并且避免了任何服务器端状态的需要:
POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 68
Cookie: session=1DQGdzYbOJQzLP7460tfyiv3do7MjyPw; csrf=R8ov2YBfTYmzFyjit8o2hKBuoIjXXVpa
csrf=R8ov2YBfTYmzFyjit8o2hKBuoIjXXVpa&email=wiener@normal-user.com
在这种情况下,如果网站包含任何cookie设置功能,则攻击者还是可以执行CSRF攻击。 在这里,攻击者无需获取自己的有效token。 他们只是发明了一个令牌(如果token会被检查,则可能生成时需要采用所需的格式),利用cookie设置行为将CSRF相关的cookie置于受害者的浏览器中,并在CSRF攻击中将token提供给受害者。
除了采用CSRF token的防御措施外,某些应用程序还使用HTTP Referer header尝试防御CSRF攻击,通常是通过验证请求是否来自应用程序自己的域来进行。 这种方法通常效果较差,并且经常会绕过。
Referer header
HTTP Referer标头(在HTTP规范中无意中被拼写错误)是一个可选的请求标头,其中包含链接到所请求资源的网页的URL。 通常,当用户触发(包括单击链接或提交表单)HTTP请求时,浏览器会自动添加它。 存在各种方法,允许链接页面保留或修改Referer标头的值。 通常是出于隐私原因才这样做。
有些应用程序只在请求携带了Referer头的时候才进行校验,如果没有携带的话则跳过校验。在这种情况下,攻击者可以以某种方式设计其CSRF攻击,使受害者用户的浏览器在请求中删除Referer标头。 有多种方法可以实现此目的,但最简单的方法是在承载CSRF攻击的HTML页面中使用META标签:
<meta name="referrer" content="no-referrer">
某些应用程序以比较幼稚的方式验证Referer标头。 例如,如果应用程序简单地验证Referer包含其自己的域名,则攻击者可以将所需的值放在URL中的其他位置:
http://attacker-website.com/csrf-attack?vulnerable-website.com
如果应用程序验证了Referer中的域是否以期望值开头,则攻击者可以将此域放置为自己域的子域:
http://vulnerable-website.com.attacker-website.com/csrf-attack