一、什么是 CSRF 攻击
CSRF 即跨站请求伪造(Cross - Site Request Forgery),简单来说,就是攻击者通过诱导用户在已登录的网站上执行非本意的操作。想象一下,你在银行网站登录后,没有退出,然后打开了一个恶意网站。这个恶意网站偷偷地向银行网站发送了一个转账请求,由于你在银行网站是已登录状态,银行网站会认为这个请求是你本人发起的,从而执行转账操作,这就是 CSRF 攻击的典型场景。
示例(使用 Java 和 Spring Boot 技术栈)
以下是一个简单的 Java Spring Boot 示例,模拟一个存在 CSRF 风险的表单提交:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class CsrfExampleController {
// 模拟一个账户余额
private int accountBalance = 1000;
// 显示转账页面
@GetMapping("/transfer")
public String showTransferPage() {
return "transfer";
}
// 处理转账请求
@PostMapping("/transfer")
@ResponseBody
public String transfer(@RequestParam("amount") int amount, @RequestParam("to") String to) {
if (amount <= accountBalance) {
accountBalance -= amount;
return "Transfer successful! New balance: " + accountBalance;
} else {
return "Insufficient funds";
}
}
}
注释:上述代码中,showTransferPage 方法用于显示转账页面,transfer 方法用于处理转账请求。但这个代码没有对 CSRF 进行防护,存在安全风险。
二、CSRF 攻击的应用场景
金融领域
在金融网站中,用户登录后可能会进行转账、支付等操作。如果网站没有有效的 CSRF 防护,攻击者可以诱导用户在已登录的情况下访问恶意网站,从而执行非用户本意的转账或支付操作,给用户带来经济损失。
社交网络
在社交网络中,攻击者可以利用 CSRF 攻击让用户在不知情的情况下发布恶意内容、关注特定账号等。例如,攻击者可以构造一个恶意链接,当用户点击该链接时,会自动在用户的社交账号上发布一条包含恶意信息的动态。
企业内部系统
企业内部的办公系统、财务管理系统等也可能成为 CSRF 攻击的目标。攻击者可以通过诱导企业员工在已登录的情况下访问恶意网站,执行如修改员工信息、删除重要数据等操作,影响企业的正常运营。
三、CSRF 攻击的技术优缺点
优点(对于攻击者而言)
- 隐蔽性强:CSRF 攻击不需要用户手动输入敏感信息,而是利用用户在其他网站的已登录状态,用户很难察觉自己正在遭受攻击。例如,用户在浏览网页时,可能只是随意点击了一个链接,就不知不觉地执行了恶意操作。
- 实施成本低:攻击者只需要构造一个包含恶意请求的链接或页面,诱导用户访问即可,不需要复杂的技术手段。
缺点(对于攻击者而言)
- 依赖用户登录状态:CSRF 攻击必须在用户已经登录目标网站的情况下才能生效。如果用户没有登录,攻击就无法成功。
- 受同源策略限制:虽然 CSRF 攻击可以跨站,但仍然受到浏览器同源策略的一定限制。某些情况下,浏览器会阻止跨站请求,从而降低攻击的成功率。
四、CSRF 攻击防护实践
使用 CSRF 令牌
CSRF 令牌是一种常用的防护 CSRF 攻击的方法。服务器在生成页面时,会为每个用户生成一个唯一的令牌,并将其嵌入到页面中。当用户提交请求时,服务器会验证请求中携带的令牌是否与服务器端存储的令牌一致。如果一致,则认为请求是合法的;否则,拒绝请求。
示例(Java 和 Spring Boot 技术栈)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
return http.build();
}
}
注释:上述代码使用 Spring Security 框架开启 CSRF 防护,CookieCsrfTokenRepository.withHttpOnlyFalse() 用于生成 CSRF 令牌并存储在 cookie 中。在前端页面中,需要将令牌添加到表单或请求头中。
验证请求来源
服务器可以通过验证请求的来源(如 Referer 头)来判断请求是否合法。如果请求的来源不是合法的网站,则拒绝请求。
示例(Java 和 Spring Boot 技术栈)
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RefererFilter extends OncePerRequestFilter {
private static final String ALLOWED_ORIGIN = "https://example.com";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String referer = request.getHeader("Referer");
if (referer != null &&!referer.startsWith(ALLOWED_ORIGIN)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid referer");
return;
}
filterChain.doFilter(request, response);
}
}
注释:上述代码实现了一个过滤器,用于验证请求的 Referer 头。如果 Referer 头不是来自允许的域名,则返回 403 错误。
同源检查
浏览器的同源策略可以在一定程度上防止 CSRF 攻击。服务器可以设置响应头,限制页面只能在同源的情况下加载。
示例(Java 和 Spring Boot 技术栈)
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SameOriginFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
response.setHeader("Content-Security-Policy", "default-src'self'");
filterChain.doFilter(request, response);
}
}
注释:上述代码实现了一个过滤器,设置了 Content - Security - Policy 响应头,限制页面只能加载来自同源的资源。
五、注意事项
令牌的安全性
CSRF 令牌必须是唯一且随机的,并且要妥善存储。如果令牌被泄露,攻击者可以利用该令牌进行 CSRF 攻击。
跨域问题
在使用验证请求来源和同源检查时,需要注意跨域问题。如果网站需要支持跨域请求,需要进行额外的配置。
兼容性问题
不同的浏览器对 CSRF 防护机制的支持可能存在差异,需要进行充分的测试,确保在各种浏览器中都能正常工作。
六、文章总结
CSRF 攻击是一种常见且危险的网络安全威胁,它可以利用用户的已登录状态执行非本意的操作。为了防护 CSRF 攻击,可以采用多种方法,如使用 CSRF 令牌、验证请求来源、同源检查等。在实施防护措施时,需要注意令牌的安全性、跨域问题和兼容性问题。通过合理的防护措施,可以有效地降低 CSRF 攻击的风险,保障网站和用户的安全。
评论