首页 > 其他分享 >Web安全之CSRF攻击详解与防护

Web安全之CSRF攻击详解与防护

时间:2024-09-09 21:51:45浏览次数:11  
标签:Web 请求 验证 用户 Token 详解 CSRF String

在互联网应用中,安全性问题是开发者必须时刻关注的核心内容之一。跨站请求伪造(Cross-Site Request Forgery, CSRF),是一种常见的Web安全漏洞。通过CSRF攻击,黑客可以冒用受害者的身份,发送恶意请求,执行诸如转账、订单提交等操作,导致严重的安全后果。

本文将详细讲解CSRF攻击的原理及其防御方法,结合电商交易系统的场景给出错误和正确的示范代码,并分析常见的安全问题与解决方案,帮助开发者全面理解和防御CSRF攻击。

1. CSRF攻击概述

1.1 CSRF的原理

CSRF攻击是指黑客通过欺骗用户在不知情的情况下向受信任的服务器发送请求,从而执行用户并未授权的操作。由于浏览器的同源策略,浏览器会自动携带当前登录用户的身份凭证(如Cookie),导致服务器误以为请求是合法用户发出的。

常见的CSRF攻击流程如下:

  1. 用户登录电商系统,并在浏览器中保持会话(比如通过Cookie保存登录状态)。
  2. 攻击者构造一个恶意网站,诱导用户访问该网站。
  3. 恶意网站通过用户的浏览器向电商系统发送请求,例如提交订单、修改账户信息等。
  4. 由于用户已经登录,服务器在收到请求时会认为是合法请求,从而执行攻击者的恶意操作。
1.2 CSRF的危害
  • 账户盗用:攻击者可以伪造请求进行账户操作,如修改密码、转账等。
  • 资金损失:在电商交易系统中,CSRF可以被用来提交伪造订单、篡改收货地址、转移资金等。
  • 信息泄露:攻击者可能通过CSRF请求获取用户的敏感信息。

2. 电商交易系统中的CSRF攻击示例

为了更直观地理解CSRF攻击的危害,我们以电商交易系统为例,演示错误的代码实现以及如何修复它。

2.1 错误示范:未防护CSRF的订单提交

在一个简单的电商交易系统中,用户可以通过提交订单来购买商品。假设服务器端的订单提交接口是通过POST请求进行的,代码如下:

// 订单提交控制器
@PostMapping("/submitOrder")
public String submitOrder(@RequestParam("productId") String productId, 
                          @RequestParam("quantity") int quantity,
                          HttpSession session) {
    // 获取当前用户信息
    User user = (User) session.getAttribute("currentUser");

    // 创建订单
    Order order = new Order();
    order.setUserId(user.getId());
    order.setProductId(productId);
    order.setQuantity(quantity);

    // 保存订单到数据库
    orderService.saveOrder(order);

    return "orderSuccess";
}

这种实现存在明显的安全问题:攻击者可以诱导用户访问恶意链接,从而提交伪造的订单。

例如,攻击者可以构造如下HTML页面,并诱导用户点击:

<html>
<body>
    <form action="http://ecommerce.com/submitOrder" method="POST">
        <input type="hidden" name="productId" value="123">
        <input type="hidden" name="quantity" value="10">
        <input type="submit" value="Submit order">
    </form>
</body>
</html>

如果用户在登录状态下点击了该页面的提交按钮,电商系统将生成一个伪造的订单,而用户对此一无所知。

2.2 错误示范:CSRF攻击的真实危害

在实际的电商系统中,攻击者可能会诱导用户执行更为严重的操作,比如修改收货地址、提交高价商品订单等。这些操作可以通过隐藏的表单字段自动完成,用户根本不需要手动提交。

2.3 CSRF防御的基本原则

防御CSRF攻击的核心在于服务器能够验证每一个请求的合法性。一般来说,CSRF防御的主要手段有:

  1. Token校验:为每一个请求生成一个唯一的Token,服务器通过该Token判断请求是否合法。
  2. Referer验证:通过检查HTTP请求头中的Referer字段,验证请求是否来自合法的页面。
  3. SameSite Cookie:通过设置Cookie的SameSite属性,限制跨站点的请求携带Cookie。

3. Token防护CSRF

3.1 使用Token防护CSRF

Token校验是防护CSRF攻击最常用且最有效的方法。它的工作原理是:

  • 在表单页面加载时,服务器生成一个唯一的Token,并将其嵌入到表单中。
  • 用户提交表单时,Token会一同提交到服务器。
  • 服务器接收到请求后,会验证该Token是否与会话中的Token一致,只有匹配时才允许执行后续操作。

以下是如何通过Token防护CSRF攻击的示例。

3.2 在表单中添加CSRF Token

首先,我们需要为每一个请求生成唯一的Token,并在提交时携带该Token。以下是Spring Boot中的CSRF防护机制的实现。

  1. 生成CSRF Token
    Spring Security默认提供了CSRF防护机制。我们可以通过以下配置启用它:

    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .csrf() // 开启CSRF防护
                .and()
                .authorizeRequests()
                .antMatchers("/submitOrder").authenticated() // 需要登录
                .and()
                .formLogin().loginPage("/login").permitAll();
        }
    }
    

    Spring Security会自动为每个页面生成一个CSRF Token,并将该Token嵌入到页面中的隐藏字段或HTTP头中。

  2. 在表单中添加CSRF Token
    Spring Security会在每个表单中自动包含一个CSRF Token。表单代码如下:

    <form action="/submitOrder" method="POST">
        <input type="hidden" name="_csrf" value="${_csrf.token}"/> <!-- 自动生成的CSRF Token -->
        <input type="text" name="productId" placeholder="Product ID"/>
        <input type="number" name="quantity" placeholder="Quantity"/>
        <input type="submit" value="Submit Order"/>
    </form>
    
  3. 服务器端验证CSRF Token
    当用户提交表单时,Spring Security会自动验证CSRF Token。如果Token验证失败,将会抛出异常,阻止请求的执行。

3.3 限制 CSRF Token 的有效期

在 CSRF 防护机制中,限制 Token 的有效期可以通过以下步骤实现:

  1. 生成带有时间戳的 Token: CSRF Token 在生成时附加一个时间戳,以标识其生成的时间。
  2. 校验 Token 时检查有效期: 在验证 CSRF Token 的同时,检查其时间戳是否在允许的时间范围内,过期的 Token 将视为无效,要求用户重新提交表单或刷新页面。
3.3.1 生成带时间戳的 CSRF Token

在生成 CSRF Token 时,我们可以在 Token 中附加一个时间戳来记录生成时间。例如,可以通过 base64 编码将随机生成的 Token 和当前时间戳一起组合。

import java.util.Base64;
import java.util.Date;

public class CsrfTokenGenerator {

    private static final long TOKEN_VALIDITY = 5 * 60 * 1000; // Token 有效期为 5 分钟

    // 生成带时间戳的 CSRF Token
    public static String generateCsrfToken() {
        String token = generateRandomToken(); // 生成随机Token
        long timestamp = System.currentTimeMillis(); // 获取当前时间戳
        String tokenWithTimestamp = token + ":" + timestamp;
        return Base64.getEncoder().encodeToString(tokenWithTimestamp.getBytes()); // Base64 编码
    }

    private static String generateRandomToken() {
        // 此处生成随机 Token,简单示例为随机UUID
        return java.util.UUID.randomUUID().toString();
    }
}
3.3.2 验证带时间戳的 CSRF Token

在服务器端对 Token 进行验证时,除了常规的 Token 匹配,还需要校验时间戳,确保 Token 在有效期内。

import java.util.Base64;

public class CsrfTokenValidator {

    private static final long TOKEN_VALIDITY = 5 * 60 * 1000; // 5 分钟有效期

    public static boolean validateCsrfToken(String token) {
        try {
            // 解码 Token
            String decodedToken = new String(Base64.getDecoder().decode(token));
            String[] parts = decodedToken.split(":");

            if (parts.length != 2) {
                return false; // Token 格式错误
            }

            String csrfToken = parts[0]; // 获取CSRF Token
            long timestamp = Long.parseLong(parts[1]); // 获取时间戳

            long currentTime = System.currentTimeMillis();
            if (currentTime - timestamp > TOKEN_VALIDITY) {
                return false; // Token 已过期
            }

            // 验证Token本身的正确性(与Session中的Token对比)
            return csrfToken.equals(getStoredToken()); // 假设getStoredToken()获取服务器端存储的Token
        } catch (Exception e) {
            return false; // 解码或校验失败
        }
    }

    private static String getStoredToken() {
        // 这里从服务器Session或者数据库中获取已存储的CSRF Token
        return "stored-token-example";
    }
}
3.3.3 CSRF Token 使用中的代码示例

在电商交易系统的具体示例中,假设用户进行购物车的结算操作。我们可以通过 CSRF Token 限制请求的有效期,防止攻击者在很久之前窃取的 Token 被再次利用。

前端发送请求:

function checkout() {
    let csrfToken = getCookie('CSRF-TOKEN');
    fetch('/checkout', {
        method: 'POST',
        headers: {
            'X-CSRF-TOKEN': csrfToken, // 包含CSRF Token
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            productId: 123,
            amount: 1
        })
    }).then(response => {
        if (response.status === 403) {
            alert('CSRF Token 过期或无效,请刷新页面再试');
        }
    });
}

服务器端验证:

在服务器端,通过 CsrfTokenValidator 对 Token 进行验证,确保其没有过期。

@PostMapping("/checkout")
public ResponseEntity<String> checkout(@RequestHeader("X-CSRF-TOKEN") String csrfToken) {
    if (!CsrfTokenValidator.validateCsrfToken(csrfToken)) {
        return new ResponseEntity<>("CSRF Token 无效或已过期", HttpStatus.FORBIDDEN);
    }
    // 处理购物车结算逻辑
    return new ResponseEntity<>("结算成功", HttpStatus.OK);
}

3.3.4 刷新过期 Token

如果用户的 CSRF Token 过期,前端可以通过一个特定的 API 进行 Token 刷新,重新获取有效的 CSRF Token。

@GetMapping("/refreshCsrfToken")
public ResponseEntity<String> refreshCsrfToken(HttpServletResponse response) {
    String newToken = CsrfTokenGenerator.generateCsrfToken();
    Cookie csrfCookie = new Cookie("CSRF-TOKEN", newToken);
    csrfCookie.setPath("/");
    csrfCookie.setHttpOnly(true);
    response.addCookie(csrfCookie);
    return new ResponseEntity<>("CSRF Token 已刷新", HttpStatus.OK);
}

3.3.5 Token 过期处理的优点
  • 安全性增强:即使攻击者获取到 CSRF Token,也只能在有限的时间内进行攻击。
  • 防止 Token 被长期滥用:定期刷新 Token,防止长期使用相同的 Token 增加攻击风险。

4. 使用Referer头验证

另一种防护CSRF攻击的方式是检查HTTP请求头中的Referer字段,验证请求是否来自同一个网站。虽然Referer验证并不是100%可靠(因为可能被用户代理修改),但可以作为一种补充手段。

我们可以通过Spring Security提供的Referer验证器实现这一机制:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .csrf()
        .requireCsrfProtectionMatcher(new AntPathRequestMatcher("/submitOrder"))
        .and()
        .authorizeRequests()
        .antMatchers("/submitOrder").authenticated();
}

通过这种方式,服务器会检查请求的来源,如果请求的来源不是本站域名,则拒绝执行该请求。

5. 同源检测(SameSite Cookie)

通过设置 cookie 的 SameSite 属性,可以防止跨站点请求时浏览器发送 cookie,从而减少 CSRF 攻击的风险。

  • SameSite=Lax: 允许导航链接发送 cookie,但跨站点 POST 请求不会发送 cookie。
  • SameSite=Strict: 完全禁止跨站点的请求发送 cookie,最大限度地防御 CSRF。

Cookie 设置示例:

http
    .csrf()
    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
    .and()
    .sessionManagement()
    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    .and()
    .addFilterBefore(new CsrfFilter(csrfTokenRepository), UsernamePasswordAuthenticationFilter.class)
    .headers()
    .frameOptions().sameOrigin()
    .httpStrictTransportSecurity()
    .includeSubDomains(true)
    .and()
    .cookie()
    .sameSite(SameSiteCookieAttributeValue.LAX);

6. 总结

本文详细介绍了CSRF攻击的原理和危害,并通过错误与正确示范,演示了如何防护CSRF攻击。CSRF攻击是一种常见且危险的安全漏洞,开发者应时刻保持警惕,采用诸如Token验证、Referer检查、SameSite Cookie等多层防护手段,确保Web应用的安全性。

通过掌握这些防护技巧,开发者可以有效抵御CSRF攻击,保护用户的个人信息与财产安全。在实际开发中,安全问题应始终放在优先位置,只有不断优化和完善,才能打造出安全、可靠的应用系统。

标签:Web,请求,验证,用户,Token,详解,CSRF,String
From: https://blog.csdn.net/weixin_39996520/article/details/142032205

相关文章

  • Vue.js 组件设计详解
    在现代Web开发中,组件化设计已经成为构建可维护和可扩展应用程序的关键策略之一。而Vue.js作为一个流行的前端框架,以其简单易用、灵活和高效的特点,成为开发者的首选之一。本文将详细介绍如何设计Vue组件,涵盖从基础到高级的概念和实践,包括组件的创建、通信、复用、优化和......
  • 栈迁移(详解)
    栈迁移原理栈迁移主要利用了leave_ret这个指令leave返回上级函数时,恢复原本的栈空间leavemovesp,ebppopebpret返回上级函数后,执行上级函数的命令ret等同于popeip(不存在这样的指令esp'''ebpret在函数的先执行到leave指令时,会将ebp处填充的地址pop到ebp中去......
  • 基于ARM芯片与OpenCV的工业分拣机器人项目设计与实现流程详解
    一、项目概述项目目标和用途本项目旨在设计和实现一套工业分拣机器人系统,能够高效、准确地对不同类型的物品进行自动分拣。该系统广泛应用于物流、仓储和制造业,能够显著提高工作效率,降低人工成本。技术栈关键词ARM芯片步进电机控制OpenCV图像识别无线通信模块......
  • shutil模块详解
    shutil模块提供了一系列高级文件操作功能,包括复制、移动、删除和搜索文件或目录。shutil模块对压缩包的处理是调用ZipFile和TarFile这两个模块来进行的。下面详细介绍并给出示例代码:1. shutil.copy(src,dst)复制文件,但不保留权限和时间戳等元数据。importshutils......
  • Javaweb-数据库连接池
    packageDRUID;importcom.alibaba.druid.pool.DruidDataSourceFactory;importjavax.sql.DataSource;importjava.io.FileInputStream;importjava.sql.Connection;importjava.util.Properties;publicclassDruidDemo{publicstaticvoidmain(String[]args)thro......
  • 数据库学习笔记(黑马-Javaweb课程)
    概述P80.课程介绍:数据库:存储和管理数据的仓库SQL:操纵做关系型数据库的编程语言数据库管理系统:DBMS,操纵和管理数据库的大型软件课程介绍:数据的的设计,数据库的操作,数据库的优化-索引P81.MySQL-概述-安装配置图文详述:MySQL的下载、安装、配置、使用_mysql下载-CSDN博客语......
  • 基于Springboot的乐享田园系统 基于Web的农村集用地管理平台 基于Vue的土地承包管理平
    ......
  • SCI论文审稿的13种状态详解
    SCI论文审稿的13种状态1.Submittedtojournal(稿件提交)代表文章提交成功。在这一阶段,需要作者确认自己所提交的文件是否符合期刊的投稿要求,包括格式、内容、摘要、参考文献等,同时也要留意期刊的审核时间和流程。2.ManuscriptreceivedbyEditorialoffice(收到稿件......
  • Linux top 命令详解
    Linuxtop命令详解主要功能和用途实时监控:提供一个实时的、动态的视图,展示系统当前的状态。进程管理:允许用户查看系统中各个进程的运行状态。资源监控:CPU使用情况:监控CPU的总体使用率、用户空间占用率、系统空间占用率等。内存使用情况:展示物理内存和交换空间(swap)的使用......
  • Web大学生网页作业成品——动漫海贼王介绍网页设计与实现(HTML+CSS)(1个页面)
    ......