API安全:CSRF
字数 1453 2025-08-29 08:31:47

API安全:CSRF防护深入解析与最佳实践

1. CSRF基础概念回顾

CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种常见的Web安全威胁,攻击者诱使用户在已认证的Web应用中执行非预期的操作。在API/微服务架构中,CSRF防护尤为重要但常被忽视。

2. 传统CORS防护的局限性

2.1 Access-Control-Allow-Origin的误解

许多开发者误以为设置Access-Control-Allow-Origin头部就能完全防御CSRF攻击,实际上:

  • 它仅阻止浏览器读取跨域响应,不会阻止请求到达服务器
  • 服务器端仍然会处理请求并执行操作
  • 对于修改数据的请求(如POST),攻击者仍可成功实施CSRF

2.2 实验验证

通过以下Servlet示例验证:

@WebServlet("/data/post")
public class PostData extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
        // 服务器端仍会处理请求
        System.out.println("Received data:"+request.getParameter("data"));
    }
}

即使设置了:

resp.addHeader("Access-Control-Allow-Origin", "abc.com");

攻击者仍可构造恶意请求,服务器会执行操作但浏览器会阻止响应读取。

3. 有效的API CSRF防护策略

3.1 严格区分HTTP方法

  • GET请求:必须设计为只读操作,绝不修改数据
  • 避免同一服务同时支持GET和POST方法
  • 遵循RESTful原则,方法语义要明确

3.2 强制Content-Type检查

对于POST/PUT等修改操作:

  1. 必须要求Content-Type: application/json
  2. 服务器端严格验证:
if (request.getMethod().equals("POST") && 
    (request.getHeader("Content-Type") == null || 
     !request.getHeader("Content-Type").toLowerCase()
        .startsWith("application/json"))) {
    resp.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT);
    return;
}

原理:浏览器对非简单请求(如修改Content-Type)会先发送OPTIONS预检请求。

3.3 谨慎配置CORS头部

常见错误配置:

// 错误示例:允许任意源OPTIONS请求但错误设置Allow-Headers
if(request.getMethod().equals("OPTIONS")){
    resp.addHeader("Access-Control-Allow-Origin", "*");
} else {
    resp.addHeader("Access-Control-Allow-Origin", "abc.com");
}
resp.addHeader("Access-Control-Allow-Headers", "Content-Type"); // 危险!

问题:在Access-Control-Allow-Headers中包含Content-Type会绕过预检保护。

3.4 预检请求(Preflight)机制

  • 浏览器对"非简单请求"会先发送OPTIONS请求
  • 触发预检的条件包括:
    • 自定义头部
    • Content-Type非以下值:
      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain

注意:IE浏览器对预检的触发条件有所不同,需要特别测试。

4. 特殊场景处理

4.1 文件上传API

挑战:

  • 必须使用multipart/form-data Content-Type
  • 不会触发预检请求
  • 传统CSRF防护手段失效

解决方案:

  1. 使用一次性令牌(CSRF Token)
  2. 检查Origin/Referer头部(非绝对可靠)
  3. 限制上传接口的调用源

4.2 微服务架构考虑

  • 确保所有服务节点采用一致的CORS策略
  • API网关统一处理安全策略
  • 避免服务间调用因CORS受阻

5. 最佳实践总结

  1. 方法分离:严格区分只读(GET)和修改(POST/PUT/DELETE)操作
  2. 内容类型强制:修改操作必须使用application/json
  3. CORS精细配置
    • 避免过度宽松的Access-Control-Allow-Headers
    • 区分OPTIONS和其他方法的源控制
  4. 预检机制理解:清楚哪些请求会触发预检
  5. 防御纵深:结合多种防护措施:
    • CSRF Tokens(传统Web应用)
    • SameSite Cookie属性
    • 关键操作二次认证
  6. 浏览器兼容性:特别是IE的特殊行为
  7. 日志监控:记录所有跨域请求尝试

6. 配置示例

安全响应过滤器示例:

@WebFilter(filterName = "secureApiFilter")
public class SecureApiFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        
        // 处理预检请求
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setHeader("Access-Control-Allow-Origin", "trusted.com");
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
            response.setHeader("Access-Control-Max-Age", "3600");
            // 谨慎设置允许的头部,不要包含Content-Type
            response.setHeader("Access-Control-Allow-Headers", "Authorization, X-Requested-With");
            return;
        }
        
        // 验证POST请求的内容类型
        if ("POST".equalsIgnoreCase(request.getMethod()) || 
            "PUT".equalsIgnoreCase(request.getMethod())) {
            String contentType = request.getHeader("Content-Type");
            if (contentType == null || !contentType.startsWith("application/json")) {
                response.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
                return;
            }
        }
        
        // 设置响应头
        response.setHeader("Access-Control-Allow-Origin", "trusted.com");
        chain.doFilter(request, response);
    }
}

通过以上多层次的防护措施,可以显著提高API服务对CSRF攻击的抵御能力,在微服务和API经济时代构建更可靠的安全防护体系。

API安全:CSRF防护深入解析与最佳实践 1. CSRF基础概念回顾 CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种常见的Web安全威胁,攻击者诱使用户在已认证的Web应用中执行非预期的操作。在API/微服务架构中,CSRF防护尤为重要但常被忽视。 2. 传统CORS防护的局限性 2.1 Access-Control-Allow-Origin的误解 许多开发者误以为设置 Access-Control-Allow-Origin 头部就能完全防御CSRF攻击,实际上: 它仅阻止浏览器读取跨域响应, 不会阻止请求到达服务器 服务器端仍然会处理请求并执行操作 对于修改数据的请求(如POST),攻击者仍可成功实施CSRF 2.2 实验验证 通过以下Servlet示例验证: 即使设置了: 攻击者仍可构造恶意请求,服务器会执行操作但浏览器会阻止响应读取。 3. 有效的API CSRF防护策略 3.1 严格区分HTTP方法 GET请求 :必须设计为 只读操作 ,绝不修改数据 避免同一服务同时支持GET和POST方法 遵循RESTful原则,方法语义要明确 3.2 强制Content-Type检查 对于POST/PUT等修改操作: 必须要求 Content-Type: application/json 服务器端严格验证: 原理 :浏览器对非简单请求(如修改Content-Type)会先发送OPTIONS预检请求。 3.3 谨慎配置CORS头部 常见错误配置: 问题 :在 Access-Control-Allow-Headers 中包含 Content-Type 会绕过预检保护。 3.4 预检请求(Preflight)机制 浏览器对"非简单请求"会先发送OPTIONS请求 触发预检的条件包括: 自定义头部 Content-Type非以下值: application/x-www-form-urlencoded multipart/form-data text/plain 注意 :IE浏览器对预检的触发条件有所不同,需要特别测试。 4. 特殊场景处理 4.1 文件上传API 挑战: 必须使用 multipart/form-data Content-Type 不会触发预检请求 传统CSRF防护手段失效 解决方案: 使用一次性令牌(CSRF Token) 检查Origin/Referer头部(非绝对可靠) 限制上传接口的调用源 4.2 微服务架构考虑 确保所有服务节点采用一致的CORS策略 API网关统一处理安全策略 避免服务间调用因CORS受阻 5. 最佳实践总结 方法分离 :严格区分只读(GET)和修改(POST/PUT/DELETE)操作 内容类型强制 :修改操作必须使用 application/json CORS精细配置 : 避免过度宽松的 Access-Control-Allow-Headers 区分OPTIONS和其他方法的源控制 预检机制理解 :清楚哪些请求会触发预检 防御纵深 :结合多种防护措施: CSRF Tokens(传统Web应用) SameSite Cookie属性 关键操作二次认证 浏览器兼容性 :特别是IE的特殊行为 日志监控 :记录所有跨域请求尝试 6. 配置示例 安全响应过滤器示例: 通过以上多层次的防护措施,可以显著提高API服务对CSRF攻击的抵御能力,在微服务和API经济时代构建更可靠的安全防护体系。