CSRF攻击与防御
概述
CSRF(Cross-site Request Forgery,跨站请求伪造),又称“一键攻击(One Click Attack)”或“会话骑劫(Session Riding)”,简称为 CSRF/XSRF。虽然其名称听起来类似于跨站脚本攻击(XSS),但二者在原理和实现方式上有本质区别。XSS 是通过攻击用户来利用网站对用户的信任,而 CSRF 是伪装成用户来欺骗网站信任用户,从而对网站发起非法请求。
相比之下,CSRF 攻击的流行程度不如 XSS,因此相关防护资源相对较少。但由于其防范难度较大,一旦被成功利用,攻击往往更隐蔽、更具危害性,尤其在与 XSS 联合使用时,后果更为严重。
简单来说,CSRF 是一种利用用户身份执行未授权操作的欺骗性攻击行为,它的主要目标是网站中的合法用户,而不是网站本身。攻击者诱导受害者在已登录受信任站点的情况下执行不知情的操作,例如转账、修改资料等。这类攻击对用户可能造成严重损失,而对网站本身的影响则相对较小。
根据实现方式,CSRF 通常可分为以下几类:
- HTML CSRF:通过提交伪造的 HTML 表单来执行操作;
- JSON Hijacking:利用网页脚本或浏览器特性,窃取敏感的 JSON 数据;
- Flash CSRF:通过 Flash 脚本发送伪造请求(如今已较少见)。
危害
一旦攻击成功,攻击者可以“伪装成你”执行一系列敏感操作,例如发送邮件、发消息、修改账户信息,甚至进行商品购买或虚拟货币转账,给个人隐私和财产安全带来严重威胁。
以下是 CSRF 攻击在现实中的一些典型用途与潜在危害:
- 对网站管理员发起攻击
利用管理员登录状态,伪造后台操作请求,如新增账户、修改权限等,甚至控制整个网站。 - 修改用户账户信息
攻击者可以修改用户的邮箱、密码、收货地址等关键信息,造成账户被盗或服务被滥用。 - 账户劫持
利用 CSRF 修改密码或绑定新的认证方式,使原用户无法访问其账户,从而实现控制。 - 传播 CSRF 蠕虫,发动大规模攻击
借助社交功能(如留言、评论),嵌入恶意请求代码,让其他访问者也遭受 CSRF 攻击,形成蠕虫式传播。 - 配合 CSRF 实施“拖库”攻击
若攻击者通过 CSRF 获得管理员权限,再利用其他漏洞下载数据库内容,将造成更大信息泄露。 - 与其他漏洞联动,形成“组合拳”攻击
CSRF 往往与 XSS、权限控制不严等漏洞联动使用,构建复杂攻击链,提高攻击成功率与破坏性。 - 针对路由器等设备的 CSRF 攻击
若家庭路由器未做身份校验,攻击者可通过 CSRF 修改其设置(如 DNS、远程管理等),导致整个网络暴露在危险中。
类型
GET类型的CSRF的检测
如果有token等验证参数,先去掉参数尝试能否正常请求。如果可以,即存在CSRF漏洞。
2、POST类型的CSRF的检测
如果有token等验证参数,先去掉参数尝试能否正常请求。如果可以,再去掉referer参数的内容,如果仍然可以,说明存在CSRF漏洞,可以利用构造外部form表单的形式,实现***。如果直接去掉referer参数请求失败,这种还可以继续验证对referer的判断是否严格,是否可以绕过。
3、特殊情况的POST类型的CSRF检测
如果上述post方式对referer验证的特别严格,有的时候由于程序员对请求类型判断不是很严格,可以导致post请求改写为get请求,从而CSRF。直接以get请求的方式进行访问,如果请求成功,即可以此种方式绕过对referer的检测,从而CSRF。
原理
CSRF(跨站请求伪造)漏洞的根本原因在于:浏览器在与网站交互时会自动携带该网站的 Cookie 信息,而不会区分请求的来源是否可信。只要用户未主动退出登录,或者浏览器未清除会话信息,浏览器就会始终默认用户处于已登录状态。
在这种情况下,如果攻击者诱导用户访问一个包含恶意请求的网站或页面(如嵌入了 CSRF 攻击代码的链接、图片、脚本等),就可能以用户的身份在用户不知情的情况下,执行某些敏感操作,比如添加账号、修改密码、转账等,而这些操作并非用户真实意图。
攻击流程模拟
- 用户 C 打开浏览器,访问受信任的网站 A,输入用户名和密码完成登录;
- 网站 A 验证通过后,在用户浏览器中生成并保存 Cookie,表示已登录;
- 用户未退出网站 A,此时又在同一浏览器中打开另一个标签页访问网站 B;
- 网站 B 返回包含恶意代码的页面,该页面尝试向网站 A 发起请求;
- 浏览器在接收到攻击代码后,会自动携带之前存储的 Cookie 信息向网站 A 发起请求;
- 网站 A 无法判断该请求是否由用户主动发起,因此会认为请求合法,并以用户 C 的身份执行操作,从而造成攻击成功。
攻击路径
示例
这是一个具有修改密码功能的页面,存在明显的 CSRF(跨站请求伪造)漏洞。
该页面通过以下 URL 实现密码修改功能:
https://dvwa.bachang.org/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#
只要用户登录状态仍在,只需在浏览器中访问该链接,密码就会被悄无声息地修改。换句话说,即使用户并未主动提交修改密码的请求,只要“点了一下链接”,攻击就已经生效了。
如下图所示,只要点击该 URL,密码立刻被更改为“123456”:
你可能会说:“我不会傻到去点击这样的链接。” 但你是否会点一个正常的短链接呢?
例如:https://t.cn/A6g2wlfu
该链接实际上是上面修改密码 URL 的短链版本。点击它,同样会悄悄修改你的密码。
攻击者还可以把该请求嵌入在网页中的一个**图片标签(<img>
)**里:
<img src="https://dvwa.bachang.org/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#" />
当你浏览含有该图片的页面时,浏览器会自动加载它,从而触发 GET 请求,密码被无声修改,而你完全察觉不到。用户根本没有任何交互操作,却已经中招。
防护
示例
这是一个存在 CSRF 漏洞的源码示例,未实现任何防护机制。
<?php
if(isset($_GET['Change'])) { // 检查是否有请求更改密码的动作// 获取用户输入的新密码和确认密码$pass_new = $_GET['password_new'];$pass_conf = $_GET['password_conf'];// 检查两次输入的密码是否匹配if ($pass_new == $pass_conf) { // 密码匹配// 防止SQL注入,转义新密码字符串$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));// 对新密码进行MD5哈希加密(注意:MD5加密在此处已经过时,不建议用于存储密码)$pass_new = md5($pass_new);// 构造SQL更新语句,更新当前登录用户(由dvwaCurrentUser()函数获取)的密码$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";// 执行SQL查询$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert) or die('<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>');// 如果密码成功更改,则反馈给用户$html .= "<pre>Password Changed.</pre>";} else {// 如果两次输入的密码不匹配,则反馈错误信息$html .= "<pre>Passwords did not match.</pre>";}// 关闭数据库连接((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
增加Referer 验证
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ])!=-1 )
这种方法只允许用户直接从本站访问修改密码的链接。如果攻击者伪造了请求头,仍然有可能绕过验证。因此,这种防护虽然能阻止一些简单的 CSRF 攻击,但并不是完全安全的。
Anti-CSRF token 检查
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
这行代码确保请求中携带了有效的 token,通过与 session 中的 token 对比,来确认请求的合法性,防止恶意网站伪造请求。
在没有其他防护的情况下,结合 XSS 漏洞 可以绕过这个机制。如果存在 存储型 XSS 漏洞,攻击者可以通过上传恶意脚本来窃取当前用户的 CSRF token。每次点击包含恶意脚本的页面时,脚本都会执行并获取 token 值,然后发送给攻击者。
输入当前密码
使用 PDO 预处理查询来验证当前密码是否正确:
$data = $db->prepare( 'SELECT password FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );
$data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR );
$data->bindParam( ':password', $pass_curr, PDO::PARAM_STR );
$data->execute();
这段代码确保了修改密码操作只能在用户提供正确的当前密码的前提下执行。如果当前密码与数据库中的记录不匹配,密码将无法修改。
验证 HTTP Referer 字段
根据 HTTP 协议,请求头中的 Referer 字段记录了该请求的来源地址。在正常情况下,访问网站受限资源的请求应该来自网站自身的页面。例如,银行网站可检查每个敏感请求(如转账请求)的 Referer 字段是否以自身域名(如 bank.example
)开头。
- 优点:实现简单,仅需在服务端添加拦截器统一验证 Referer 字段,无需修改现有业务逻辑,适用于老系统。
- 缺点:Referer 可被部分浏览器禁用或伪造,安全性较弱;某些合法请求可能因跨域或跳转丢失 Referer 而被误判为攻击。
使用 Token 验证机制
这是目前最广泛采用的防御机制之一。其核心思想是在每次敏感请求中添加一个随机生成的、不可预测的 Token,并在服务端进行验证。
示例代码片段如下所示,表单中隐藏字段包含 Session 中的 token:
<input type="hidden" name="token" value="{$_SESSION['token']}" />
每次用户提交表单时,前端会将 token 一并提交到后台,服务器再将其与 Session 中存储的 token 进行比对。
- 优点:Token 是随机生成的,攻击者难以获取,防护效果可靠。
- 缺点:对系统开发者要求较高,需要为每个敏感请求生成并验证 token。
自定义 HTTP 头部字段验证
与传统 Token 不同,此方法通过 JavaScript(如 XMLHttpRequest
或 fetch
)设置自定义 HTTP Header,并在请求中添加 token。例如:
xhr.setRequestHeader("X-CSRF-Token", token);
服务器端验证请求头中的 token 即可。由于浏览器的同源策略,不允许跨站请求设置自定义请求头,因此这种方式可有效阻止 CSRF。
- 优点:更安全,token 不易通过 Referer 或 URL 泄露;适合前后端分离项目。
- 缺点:仅适用于 Ajax 请求,不适用于传统的表单提交;对旧系统改造成本较高。
使用 SameSite Cookie 属性限制跨站请求
SameSite
属性是浏览器层面为防御 CSRF 而设计的 Cookie 策略:
Strict
:完全禁止第三方网站携带 Cookie,最安全;Lax
:允许部分跨站请求携带 Cookie(如 GET 请求、超链接跳转);None
:允许任何情况下携带 Cookie,需配合Secure
使用。
设置方式示例如下:
Set-Cookie: SID=abc123; SameSite=Strict; Secure; HttpOnly
在 Tomcat 中配置:
<Context><CookieProcessor sameSiteCookies="lax" />
</Context>
- 优点:浏览器原生支持,不需要更改业务代码;
- 缺点:对旧版本浏览器兼容性较差,可能影响正常跨域业务流程。
参考文章
- 网络安全之CSRF漏洞原理和实战,以及CSRF漏洞防护方法https://blog.csdn.net/Scalzdp/article/details/134196503
- CSRF漏洞https://www.cnblogs.com/tomyyyyy/p/14581871.html
- CSRF通关(高中低)-DVWA通关指南 Cross Site Request Forgery (CSRF)https://blog.csdn.net/lza20001103/article/details/145523786