跳到主要内容

CSRF 跨站请求伪造

CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种利用用户已登录的身份,在用户不知情的情况下执行非授权操作的攻击方式。本章将详细介绍 CSRF 的原理和防护方法。

什么是 CSRF?

CSRF 攻击利用了 Web 应用的身份验证机制。当用户登录某个网站后,浏览器会保存该网站的会话 Cookie。攻击者可以诱导用户访问一个恶意页面,该页面会自动向目标网站发送请求,利用用户的登录状态完成非法操作。

CSRF 攻击原理

攻击流程

1. 用户登录网站 A(如银行网站)
2. 浏览器保存会话 Cookie
3. 用户访问攻击者控制的网站 B
4. 网站 B 包含向网站 A 发送请求的代码
5. 浏览器自动携带网站 A 的 Cookie
6. 网站 A 认为请求来自合法用户,执行操作

攻击示例

假设银行网站的转账接口:

<!-- 银行转账表单 -->
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="to" value="attacker-account">
<input type="hidden" name="amount" value="10000">
</form>

攻击者可以构造恶意页面:

<!-- 恶意页面 -->
<html>
<body>
<h1>点击这里赢取大奖!</h1>
<!-- 隐藏的表单,自动提交 -->
<form action="https://bank.com/transfer" method="POST" id="csrf-form">
<input type="hidden" name="to" value="attacker-account">
<input type="hidden" name="amount" value="10000">
</form>
<script>
document.getElementById('csrf-form').submit();
</script>
</body>
</html>

当已登录的用户访问这个页面时,转账请求会自动发送。

CSRF 的危害

CSRF 攻击可以导致:

  1. 资金转移 - 从银行账户转账
  2. 数据修改 - 更改用户设置、邮箱等
  3. 权限提升 - 添加管理员账户
  4. 删除数据 - 删除重要信息
  5. 发布内容 - 发布恶意信息

CSRF 防护

1. CSRF Token

最常用的防护方法是使用 CSRF Token:

// 生成 CSRF Token
String csrfToken = UUID.randomUUID().toString();
session.setAttribute("csrf_token", csrfToken);

// 在表单中添加隐藏字段
<input type="hidden" name="_csrf" value="${csrfToken}" />

// 验证 Token
String requestToken = request.getParameter("_csrf");
String sessionToken = (String) session.getAttribute("csrf_token");

if (!csrfToken.equals(requestToken)) {
throw new SecurityException("CSRF 攻击检测");
}

2. Spring Security CSRF 防护

Spring Security 默认启用 CSRF 防护:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf() // 默认启用
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
// ...
}
}

3. 验证请求头

检查请求的来源:

// 验证 Referer 和 Origin 头
String origin = request.getHeader("Origin");
String referer = request.getHeader("Referer");

if (origin != null && !isTrustedOrigin(origin)) {
throw new SecurityException("不信任的来源");
}

使用 SameSite 属性防止 CSRF:

Cookie cookie = new Cookie("SESSION_ID", sessionId);
cookie.setSameSite("Strict"); // 最严格
// 或
cookie.setSameSite("Lax"); // 较宽松,但更实用
response.addCookie(cookie);

SameSite 有三个值:

  • Strict:完全阻止跨站请求
  • Lax:允许部分跨站请求(如链接)
  • None:不限制(需要 Secure=true)

将 Token 同时放在 Cookie 和请求参数中:

// 设置 Cookie
document.cookie = "csrf-token=" + token + "; path=/";

// 请求时同时发送
fetch('/api/data', {
method: 'POST',
headers: {
'X-CSRF-Token': getCookie('csrf-token')
}
});

检测 CSRF 漏洞

手动测试

  1. 登录应用
  2. 记录关键操作(如修改密码、转账)
  3. 构造恶意页面,尝试触发相同操作
  4. 检查是否成功执行

自动化工具

  • OWASP ZAP:自动检测 CSRF 漏洞
  • Burp Suite:CSRF 检测功能

最佳实践

  1. 启用 CSRF 防护 - 始终使用 CSRF Token
  2. 使用 SameSite Cookie - 为会话 Cookie 设置 SameSite
  3. 验证请求来源 - 检查 Origin 和 Referer
  4. 敏感操作验证 - 重要操作需要额外确认
  5. 避免使用 GET 修改数据 - 使用 POST/PUT 进行状态修改

小结

CSRF 攻击利用用户的登录状态,在用户不知情的情况下执行非授权操作:

  1. 攻击原理

    • 诱导用户访问恶意页面
    • 自动发送携带认证 Cookie 的请求
    • 服务器无法区分是否为用户主动操作
  2. 防护措施

    • CSRF Token(最有效)
    • SameSite Cookie
    • 验证请求来源
    • 双重提交验证
  3. 最佳实践

    • 始终启用 CSRF 防护
    • 使用框架自带的安全功能
    • 敏感操作增加额外验证