首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >CSRF入门指南:你的操作真的是你发出的吗?

CSRF入门指南:你的操作真的是你发出的吗?

作者头像
程序员三明治
发布2025-12-18 20:41:15
发布2025-12-18 20:41:15
2960
举报
文章被收录于专栏:码力up码力up

👨‍💻程序员三明治个人主页 🔥 个人专栏: 《设计模式精解》 《重学数据结构》

🤞先做到 再看见!


一、什么是CSRF?

CSRF(Cross-Site Request Forgery),中文译为“跨站请求伪造”,也被称为“one-click attack”(一键攻击)或“session riding”(会话挟持)。它是一种网络攻击方式,攻击者利用用户在当前已登录的 Web 应用程序中的身份,在用户不知情的情况下执行非本意的操作。

简单来说,CSRF 攻击的原理是:攻击者盗用用户的登录凭据,冒充用户身份向服务器发送请求。由于这些请求携带了合法的用户凭证(如 Cookie),服务器会将其视为正常操作并执行。攻击者可以通过这种方式实现多种恶意行为,例如以用户名义发送邮件或消息、窃取账号信息、添加管理员权限,甚至进行商品购买或虚拟货币转账等。

在实际攻击中,攻击者往往借助社会工程学手段,例如通过聊天软件(如 QQ)发送伪装成可信链接的恶意网址(有时还会使用短链接以隐藏真实地址),诱使用户点击。一旦用户点击链接,攻击者预设的操作便会在用户已登录的环境中自动执行。

因此,CSRF 攻击对终端用户的数据安全和操作权限构成了严重威胁。尤其当受害用户具备高级权限(如管理员账户)时,此类攻击可能会危及整个 Web 应用系统的安全。

二、CSRF原理

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 防御

流程图如下:

三介绍两种简单地CSRF攻击行为

3.1、GET 示例

假如下面这个 GET 请求,用于一个已登录的用户向指定的银行账户 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">1234</font> 转账:

代码语言:javascript
复制
GET http://bank.com/transfer?accountNo=1234&amount=100

如果攻击者想把钱从受害者的账户转到自己的账户(<font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">5678</font>),他需要让受害者触发请求:

代码语言:javascript
复制
GET http://bank.com/transfer?accountNo=5678&amount=1000

有多种方法可以实现这一点:

  • 链接 - 攻击者可以说服/诱导受害者点击该链接,例如执行转账:
代码语言:javascript
复制
<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 作为图片来源。换句话说,甚至不需要点击。请求将在页面加载时自动执行:
代码语言:javascript
复制
<img src="http://bank.com/transfer?accountNo=5678&amount=1000"/>

所以,涉及到敏感的业务,千万不能用 GET 请求。

3.2、POST 示例

假设转账 API 是一个 POST 请求。

代码语言:javascript
复制
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>

代码语言:javascript
复制
<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 自动提交表单:

代码语言:javascript
复制
<body onload="document.forms[0].submit()">
<form>
...
3.3、实战

在 Spring 应用中模拟 CSRF 攻击。

创建一个 “银行” 应用,定义一个转账 API <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">BankController</font>

代码语言:javascript
复制
@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 页面来触发银行转账操作:

代码语言:javascript
复制
<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>

现在、来看看攻击者页面的样子:

代码语言:javascript
复制
<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 对银行应用进行身份认证。

首先,访问银行应用页面:

代码语言:javascript
复制
http://localhost:8081/spring-rest-full/csrfHome.html

它将在浏览器上设置 <font style="color:rgb(204, 51, 51);background-color:rgb(245, 245, 245);">JSESSIONID</font> cookie。

然后访问攻击者应用:

代码语言:javascript
复制
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 会将它们视为来自银行页面的请求进行身份认证。

四、SpringSecurity开启启用/禁用CSRF

4.1、Spring Security 配置

在旧版 XML 配置(Spring Security 4 之前)中,CSRF 保护默认是禁用的,可以根据需要启用它:

代码语言:javascript
复制
<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> 属性中。

如果需要,可以禁用此配置:

代码语言:javascript
复制
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
      .csrf().disable();
    return http.build();
}
4.2、客户端配置

现在,需要在请求中包含 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:

代码语言:javascript
复制
<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 名称:

代码语言:javascript
复制
<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> 标签值:

代码语言:javascript
复制
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");

最后,使用这些值来设置 XHR Header:

代码语言:javascript
复制
$(document).ajaxSend(function(e, xhr, options) {
    xhr.setRequestHeader(header, token);
});

无状态 API 是否需要 CSRF 保护?

如果无状态 API 使用基于 Token 的身份验证(如 JWT),就不需要 CSRF 保护。反之,如果使用 Session Cookie 进行身份验证,就需要启用 CSRF 保护

如果我的内容对你有帮助,请辛苦动动您的手指为我点赞,评论,收藏。感谢大家!!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、什么是CSRF?
  • 二、CSRF原理
  • 三介绍两种简单地CSRF攻击行为
    • 3.1、GET 示例
    • 3.2、POST 示例
    • 3.3、实战
  • 四、SpringSecurity开启启用/禁用CSRF
    • 4.1、Spring Security 配置
    • 4.2、客户端配置
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档