一、CSRF概述
利用受害者尚未失效的身份认证信息,诱骗其点击恶意链接或者包含攻击代码的页面,在受害人不知情的情况下以受害者的身份向服务器发送请求,完成危险操作。
攻击原理如下图:
1、身份认证信息尚未失效:
受害者在使用账号密码登录网站A的时候,服务器会返回给浏览器一个保存受害者登录信息的cookie,这个cookie在用户尚未退出网站页面的时候,可能是一直有效的。
2、受害者点击危险页面:
受害者在没有退出网站的时候,cookie有效,这个时候访问了攻击者构造的页面。攻击者构造的页面可能是向服务器发送什么请求,进行危险操作。
3、以受害者身份访问服务器:
攻击者构造的网站虽然无法直接获取到受害者的cookie,但是cookie是保存在浏览器的,攻击者构造的向服务器发送请求的网站,受害者在同一个网站打开,那么这个时候这个请求就带着受害者的cookie去访问服务器了。
二、CSRF攻击条件
实现CSRF攻击,必须具备三个关键条件:
1、相关操作
攻击者诱使应用程序中发生某种操作作。这可能是权限操作(例如,修改其他用户的权限),也可能是针对用户特定数据的任何操作(例如,更改用户自己的密码)。
2、基于Cookie的会话处理
执行该操作涉及发出一个或多个HTTP请求,并且该应用程序仅依靠会话cookie来标识发出请求的用户,没有其他机制可以跟踪会话或验证用户请求。
3、没有不可预测的请求参数
执行该操作的请求不包含攻击者无法确定或猜测其值的任何参数。例如,当使用户更改密码时,如果攻击者需要知道现有密码的值,则该功能不会受到攻击。
例如,假设一个应用程序包含一个功能,该功能使用户可以更改其帐户上的电子邮件地址,用户执行此操作时,他们将发出如下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所需的条件:
1、攻击者会更改用户帐户上的电子邮件地址,执行此操作后,攻击者通常将能够触发密码重置并完全控制用户的帐户。
2、该应用程序使用会话cookie来标识哪个用户发出了请求,没有其他令牌或机制来跟踪用户会话。
3、攻击者可以轻松确定执行操作所需的请求参数的值。
在满足这些条件的情况下,攻击者可以构建包含以下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>
如果受害用户访问攻击者的网页,则会发生以下情况:
1、攻击者的页面将触发对易受攻击的网站的HTTP请求。
2、如果用户登录到易受攻击的网站,则其浏览器将自动在请求中包括其会话cookie。
3、易受攻击的网站将以正常方式处理请求,将其视为由受害者用户发出,并更改其电子邮件地址。
我们通常以基于cookie的会话处理来描述CSRF,但它也出现在应用程序自动向请求中添加一些用户凭据的其他情况下,例如HTTP 基于身份验证和基于证书的身份验证。
三、CSRF的防御
目前防御CSRF的方法主要有三种:
1、验证请求的Referer值
2、CSRF Token
3、验证码等验证业务过程的方式
1、验证请求的Referer值
由于CSRF的请求发起方是攻击者,因此在Referer处,攻击者与当前用户所处的界面完全不同。若Referer是以自己的网站开头的域名,则说明该请求来自己网站自己,是合法,如果Referer为空或者是其他网站域名,就有可能是CSRF攻击。
但是在某些情况下。Referer验证存在缺陷,那么可以利用各种伪造的方式实现对Referer验证的绕过。一般推荐利用referer来监控CSRF行为,但作为防御,并不一定是安全的。
2、CSRF Token
CSRF之所以可以攻击成功,是因为攻击者可以伪造用户的请求,那么,如果我们加入一个参数,让攻击者无法去伪造,那么便可以防御CSRF。例如,我们可以在请求中以参数的形式加入一个随机产生的token,并在服务器端验证token,如果没有token或者token的内容不正确,则认为该请求可能是CSRF攻击。
Token在当前用户第一次访问某项功能页面时生成,且Token是一次性的,在生成完毕后由服务器端发送给客户端,用户接收Token之后,会在进行下一步业务时提交Token,并由服务器进行有效性验证。由于攻击者在CSRF利用时,无法获取当前用户的Token,导致验证失败,则估计请求无法成功。
以下为一个添加Token功能的示例代码:
session_start();
function token_start(){
$_SESSION['token'] = md5(rand(1,10000));
}
function token_check(){
$return = $_REQUEST['token']===$_SESSION['token'] ? true : false;
token_start();
return $return;
}
//如果token为空,则生成一个token
if(!isset($_SESSION['token'])||$_SESSION['token']==''){
token_start();
}
if(isset($_POST['test'])){
if(!valid_token()){
echo "token fail";
}else{
echo 'success,value:'.$_POST['test'];
}
}
生成token的方式非常灵活,这里以10000以内的随机数的md5值作为token为例,在每次访问后,无论请求失败还是成功,token都会更新。
在使用token时需要遵循以下原则:
1、toekn必须为一次性的,无论该业务请求失败还是成功,每次用户请求时都需要重置token。
2、生成token时,需有较强的随机性,避免采取简单的可预测的方式。
3、验证码等验证业务过程的方式
CSRF攻击的过程,往往是在用户不知情的情况下构造了网络请求。而加入验证码,则强制用户必须和应用进行交互,但是这种方法弊端很多,因为网站不能给所有请求页面加入验证码,可能会破坏用户体验,所以验证码只能作为一个辅助手段去进行防御CSRF。
四、基于Token的CSRF
目前大多数的CSRF漏洞是由CSRF令牌验证错误引起的。
示例:应用程序现在在更改用户密码的请求中包含CSRF令牌:
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
该请求中,应用程序不仅依赖Cookie进行会话处理,并且请求中包含一个csrf参数,攻击者无法确定该参数的值。但是,可以通过多种方式来打破防御。
1、CSRF令牌的验证取决于请求方法
当请求使用POST方法时,引用程序对令牌进行验证,但是当使用GET方法时,跳过了验证。
在这种情况下,攻击者可以切换到GET方法来绕过验证并发送CSRF攻击:
GET /my-account/change-email?email=dsadadasd%40qq.com&csrf=JJ9OYFy2ueYMyTt1IU6zxlEytjH9fNGQ HTTP/1.1
Host: ac941f271e2b9cb680120d0500620068.web-security-academy.net
Origin: https://ac941f271e2b9cb680120d0500620068.web-security-academy.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: https://ac941f271e2b9cb680120d0500620068.web-security-academy.net/my-account
Cookie: session=T6qoNIPuPLfVAg8Oex7m18WKN6SU7xk6
2、CSRF令牌的验证取决于令牌是否存在
如果令牌存在,某些应用程序会正确验证令牌,但是如果省略令牌,则跳过验证。
在这种情况下,攻击者可以删除包含令牌的整个参数(而不仅仅是令牌的值),以绕过验证并进行CSRF攻击:
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
3、CSRF令牌未绑定到用户会话
某些应用程序不验证令牌与发出请求的用户是否属于同一会话,而是去检测并接受出现在该池中的所有令牌。在这种情况下,攻击者可以使用自己的帐户登录到应用程序,获取有效令牌,然后在其CSRF攻击中将该令牌提供给受害用户。
4、CSRF令牌绑定到非会话cookie
在上述漏洞的一种变体中,某些应用程序确实将CSRF令牌绑定到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的任何行为,则可能构成攻击。攻击者可以使用自己的帐户登录到应用程序,获取有效的令牌和关联的cookie,利用cookie的设置行为将其cookie置于受害者的浏览器中,并在CSRF攻击中将其令牌提供给受害者。
cookie设置行为甚至不必与CSRF漏洞存在于同一Web应用程序中。如果所控制的Cookie具有适当的范围,则可以利用同一总体DNS域中的任何其他应用程序在目标应用程序中设置Cookie。例如,staging.demo.normal-website.com可以利用Cookie设置功能来放置提交到的Cookie secure.normal-website.com。
5、CSRF令牌只是在Cookie中重复
在上述漏洞的另一种变体中,某些应用程序不维护任何已发布令牌的服务器端记录,而是复制cookie和request参数中的每个令牌。当后续请求得到验证时,应用程序只需验证请求参数中提交的令牌是否与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攻击。在这里,攻击者无需获取自己的有效令牌。他们只是发明了一个令牌(如果正在检查,则可能采用所需的格式),利用cookie设置行为将其cookie置于受害者的浏览器中,并在CSRF攻击中将令牌提供给受害者。
五、基于Referer的CSRF
某些应用程序利用HTTP Referer请求头尝试防御CSRF攻击,通过验证请求是否来自应用程序自己的域来进行。这种方法通常效果较差,并且经常会被绕过。
以referer为此域名为例:
referer:http://vulnerable-website.com
1、Referer的验证取决于请求头是否存在
某些应用程序Referer会在请求中显示标头时对其进行验证,但是如果省略标头,则会跳过验证。我们可以直接删除Referer请求头,观察是否可以进行绕过。
2、是否已预定义域名开头
可以将其作为自己的子域:
http://vulnerable-website.com.attacker-website.com/csrf-attack
3、是否包含自己的域名
攻击者可以在URL中的其他位置放置所需的值:
http://attacker-website.com/csrf-attack?vulnerable-website.com
4、如果referer为空的时候
可使用ftp://,http://,https://,file://,javascript:,data:。
这个时候浏览器地址栏是file://开头的,如果这个HTML页面向任何http站点提交请求的话,这些请求的Referer都是空的。
六、XSS与CSRF
XSS和CSRF有什么区别?
1、跨站脚本(XSS)允许攻击者在受害者用户的浏览器中执行任意JavaScript。
跨站请求伪造(CSRF)允许攻击者诱使受害用户执行他们不打算执行的操作。
2、XSS漏洞的后果通常比CSRF漏洞更为严重:
CSRF通常仅适用于用户能够执行的操作的子集。通常,许多应用程序都实现CSRF防御,仅仅忽略部分操作。相反,成功的XSS利用通常可以诱使用户执行用户能够执行的任何操作,而不考虑出现漏洞的功能。
3、CSRF可以描述为“单向”漏洞,因为尽管攻击者可以诱使受害者发出HTTP请求,但他们无法从该请求中获取响应。相反,XSS是“双向”的,因为攻击者的注入脚本可以发出任意请求,读取响应并将数据泄露到攻击者选择的外部域。
4、CSRF令牌防止XSS攻击?
通过有效使用CSRF令牌,确实可以防止某些XSS攻击。考虑一个简单的反射XSS漏洞,可以像这样简单地加以利用:
https://insecure-website.com/status?message=<script>/*+Bad+stuff+here...+*/</script>
现在,假设易受攻击的函数包含一个CSRF令牌:https://insecure-website.com/status?csrf-token=CIwNZNlR4XbisJF39I8yWnWX9wX4WFoz&message=<script>/*+Bad+stuff+here...+*/</script>
假设服务器正确地验证了CSRF令牌,并拒绝了没有有效令牌的请求,那么该令牌确实可以防止XSS漏洞的利用,通过阻止攻击者伪造跨站点请求,该应用程序可以防止对XSS漏洞的小小的利用。
①如果在不受CSRF令牌保护的功能内,站点上其他任何地方都存在反射的XSS漏洞,则可以按常规方式利用该XSS。
②如果站点上任何地方都存在可利用的XSS漏洞,则可以利用该漏洞使受害用户执行操作,即使这些操作本身受CSRF令牌保护也是如此。在这种情况下,攻击者的脚本可以请求相关页面获得有效的CSRF令牌,然后使用该令牌执行受保护的操作。
③CSRF令牌不能防止存储的XSS漏洞。如果受CSRF令牌保护的页面也是存储的XSS漏洞的输出点,则可以以通常的方式利用该XSS漏洞,并且当用户访问该页面时,将执行XSS有效负载。
七、CSRF Token
1、什么是CSRF令牌?
CSRF令牌是服务器端应用程序生成的唯一的、秘密的、不可预测的值,并传输到客户端,使得它包含在客户端发出的后续HTTP请求中,发出以后的请求时,服务器端应用程序将验证该请求是否包含预期的令牌,如果令牌丢失或无效,则拒绝该请求。
CSRF令牌由于其唯一性、秘密性、不可预测性,可以使攻击者无法构造完全有效的HTTP请求,从而防止CSRF攻击。由于攻击者无法确定或预测用户CSRF令牌的值,因此他们无法使用应用程序满足请求所必需的所有参数构造请求。
2、CSRF令牌应如何生成?
CSRF令牌包含很大的熵(熵值越大,可能的事件越多,不确定性越高),是高度不可预测的,并且通常具有与会话令牌相同的属性。
您一般使用加密强度伪随机数生成器(PRNG),该生成器附带创建时的时间戳以及静态密码。
如果除了PRNG的功能之外还需要进一步的保证,则可以通过将其输出与某些用户特定的熵连接起来来生成单独的令牌,并对整个结构进行大量哈希处理。这给尝试基于颁发给令牌的样本分析令牌的攻击者带来了额外的障碍。
3、CSRF令牌应如何发送?
CSRF令牌应被视为机密,并在其整个生命周期中以安全的方式进行处理。通常有效的方法是在使用POST方法提交的HTML表单的隐藏字段内将令牌传输给客户端。提交表单后,令牌将作为请求参数包括在内:<input type="hidden" name="csrf-token" value="CIwNZNlR4XbisJF39I8yWnWX9wX4WFoz" />
为了提高安全性,包含CSRF令牌的字段应尽可能早地放置在HTML文档中,最好放在任何非隐藏的输入字段之前,以及将用户可控制的数据嵌入HTML内的任何位置之前,这减轻了攻击者可以使用精心制作的数据来操纵HTML文档并捕获其内容的各种技术。
将令牌放入URL查询字符串中不太安全,因为查询字符串为:
①记录在客户端和服务器端的各个位置;
②易于在HTTP Referer标头中传输给第三方;
③可能在用户浏览器中显示在屏幕上。
某些应用程序在自定义请求标头中传输CSRF令牌,这为试图预测或捕获另一个用户令牌的攻击者提供了进一步的方法,因为浏览器通常不允许跨域发送自定义标头,但是,该方法将应用程序限制为使用XHR(与HTML表单相反)发出受CSRF保护的请求,并且在许多情况下可能被认为过于复杂。
CSRF令牌不应在Cookie中传输。
4、CSRF令牌应如何验证?
生成CSRF令牌后,应将其存储在服务器端的用户会话数据中,当收到需要验证的后续请求时,服务器端应用程序应验证该请求是否包含与用户会话中存储的值匹配的令牌。无论请求的HTTP方法或内容类型如何,都必须执行此验证。如果该请求根本不包含任何令牌,则应以与存在无效令牌时相同的方式拒绝该请求。