👨💻程序员三明治:个人主页 🔥 个人专栏: 《设计模式精解》 《重学数据结构》
🤞先做到 再看见!
CSRF(Cross-Site Request Forgery),中文译为“跨站请求伪造”,也被称为“one-click attack”(一键攻击)或“session riding”(会话挟持)。它是一种网络攻击方式,攻击者利用用户在当前已登录的 Web 应用程序中的身份,在用户不知情的情况下执行非本意的操作。
简单来说,CSRF 攻击的原理是:攻击者盗用用户的登录凭据,冒充用户身份向服务器发送请求。由于这些请求携带了合法的用户凭证(如 Cookie),服务器会将其视为正常操作并执行。攻击者可以通过这种方式实现多种恶意行为,例如以用户名义发送邮件或消息、窃取账号信息、添加管理员权限,甚至进行商品购买或虚拟货币转账等。
在实际攻击中,攻击者往往借助社会工程学手段,例如通过聊天软件(如 QQ)发送伪装成可信链接的恶意网址(有时还会使用短链接以隐藏真实地址),诱使用户点击。一旦用户点击链接,攻击者预设的操作便会在用户已登录的环境中自动执行。
因此,CSRF 攻击对终端用户的数据安全和操作权限构成了严重威胁。尤其当受害用户具备高级权限(如管理员账户)时,此类攻击可能会危及整个 Web 应用系统的安全。
1、用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
2、在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
3、用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
4、网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
5、浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
CSRF 攻击的三个条件 :
1 . 用户已经登录了站点 A,并在本地记录了 cookie
2 . 在用户没有登出站点 A 的情况下(也就是 cookie 生效的情况下),访问了恶意攻击者提供的引诱危险站点 B (B 站点要求访问站点A)。
3 . 站点 A 没有做任何 CSRF 防御

流程图如下:

假如下面这个 GET 请求,用于一个已登录的用户向指定的银行账户 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">1234</font> 转账:
GET http://bank.com/transfer?accountNo=1234&amount=100如果攻击者想把钱从受害者的账户转到自己的账户(<font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">5678</font>),他需要让受害者触发请求:
GET http://bank.com/transfer?accountNo=5678&amount=1000有多种方法可以实现这一点:
<a href="http://bank.com/transfer?accountNo=5678&amount=1000">
点击展示美女图片
</a><font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);"><img/></font> 标签,将目标 URL 作为图片来源。换句话说,甚至不需要点击。请求将在页面加载时自动执行:<img src="http://bank.com/transfer?accountNo=5678&amount=1000"/>所以,涉及到敏感的业务,千万不能用 GET 请求。
假设转账 API 是一个 POST 请求。
POST http://bank.com/transfer
accountNo=1234&amount=100在这种情况下,<font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);"><a></font> 和 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);"><img/></font> 标签都不起作用。
攻击者需要使用 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);"><form></font>:
<form action="http://bank.com/transfer" method="POST">
<input type="hidden" name="accountNo" value="5678"/>
<input type="hidden" name="amount" value="1000"/>
<input type="submit" value="Show Kittens Pictures"/>
</form>然后,使用 JavaScript 自动提交表单:
<body onload="document.forms[0].submit()">
<form>
...在 Spring 应用中模拟 CSRF 攻击。
创建一个 “银行” 应用,定义一个转账 API <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">BankController</font>:
@Controller
public class BankController {
private Logger logger = LoggerFactory.getLogger(getClass());
@RequestMapping(value = "/transfer", method = RequestMethod.GET)
@ResponseBody
public String transfer(@RequestParam("accountNo") int accountNo,
@RequestParam("amount") final int amount) {
logger.info("Transfer to {}", accountNo);
...
}
@RequestMapping(value = "/transfer", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public void transfer2(@RequestParam("accountNo") int accountNo,
@RequestParam("amount") final int amount) {
logger.info("Transfer to {}", accountNo);
...
}
}还需要一个基本的 HTML 页面来触发银行转账操作:
<html>
<body>
<h1>CSRF test on Origin</h1>
<a href="transfer?accountNo=1234&amount=100">Transfer Money to John</a>
<form action="transfer" method="POST">
<label>Account Number</label>
<input name="accountNo" type="number"/>
<label>Amount</label>
<input name="amount" type="number"/>
<input type="submit">
</form>
</body>
</html>这是在银行应用上运行的客户端页面。
如上,通过一个简单的链接实现了 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">GET</font>,通过一个简单的 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);"><form></font> 实现了 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">POST</font>。
现在、来看看攻击者页面的样子:
<html>
<body>
<a href="http://localhost:8080/transfer?accountNo=5678&amount=1000">Show Kittens Pictures</a>
<img src="http://localhost:8080/transfer?accountNo=5678&amount=1000"/>
<form action="http://localhost:8080/transfer" method="POST">
<input name="accountNo" type="hidden" value="5678"/>
<input name="amount" type="hidden" value="1000"/>
<input type="submit" value="Show Kittens Picture">
</form>
</body>
</html>该页面在不同的应用上运行,即攻击者的应用。
最后,在本地运行银行应用和攻击者应用。
要使攻击奏效,用户需要使用 Session cookie 对银行应用进行身份认证。
首先,访问银行应用页面:
http://localhost:8081/spring-rest-full/csrfHome.html它将在浏览器上设置 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">JSESSIONID</font> cookie。
然后访问攻击者应用:
http://localhost:8081/spring-security-rest/api/csrfAttacker.html追踪源自此页面的请求,能够发现那些针对银行应用的请求。由于 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">JSESSIONID</font> Cookie 会自动随这些请求一起提交,Spring 会将它们视为来自银行页面的请求进行身份认证。
在旧版 XML 配置(Spring Security 4 之前)中,CSRF 保护默认是禁用的,可以根据需要启用它:
<http>
...
<csrf />
</http>从 Spring Security 4.x 开始,默认启用 CSRF 保护。
该默认配置将 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">CSRF</font> Token 添加到名为 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">_csrf</font> 的 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">HttpServletRequest</font> 属性中。
如果需要,可以禁用此配置:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable();
return http.build();
}现在,需要在请求中包含 CSRF Token。
<font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">_csrf</font> 属性包含以下信息:
<font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">token</font> - CSRF Token 值<font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">parameterName</font> - HTML 表单参数的名称,其中必须包含 Token 值<font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">headerName</font> - HTTP Header 的名称,其中必须包含 Token 值如果视图使用 HTML 表单,可以使用 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">parameterName</font> 和 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">token</font> 值添加隐藏 input:
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>如果视图使用 JSON,则需要使用 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">headerName</font> 和 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">token</font> 值添加 HTTP 请求头信息。
首先在 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">meta</font> 标签中包含 Token 值和 Header 名称:
<meta name="_csrf" content="${_csrf.token}"/>
<meta name="_csrf_header" content="${_csrf.headerName}"/>然后,用 JQuery 获取 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">meta</font> 标签值:
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");最后,使用这些值来设置 XHR Header:
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});无状态 API 是否需要 CSRF 保护?
如果无状态 API 使用基于 Token 的身份验证(如 JWT),就不需要 CSRF 保护。反之,如果使用 Session Cookie 进行身份验证,就需要启用 CSRF 保护
如果我的内容对你有帮助,请辛苦动动您的手指为我点赞,评论,收藏。感谢大家!!