当前位置: 首页 > news >正文

在AI编程时代,了解CSRF

前言

我写这篇文章,是在2026年,这个时候,编程智能体已经能够完成人类布置的80%以上的编程任务。特别是在很多自媒体的加持下,“人人会编程”的理念充斥着社会空间的每一个角落。的确,我们能够让Codex,Claude Code,Qoder,CodeWhale这样的编程智能体,将我们的需求(自然语言)翻译成程序代码,这些程序代码不仅能跑起来,其展示的效果还相当”惊人“。
可作为程序员,要保持冷静,我们的责任远不是辅助编程智能体来生成代码。当大家都在为编程智能体生成代码的“看得见效果”而欢呼的同时,我们要比之前任何时候都要了解那些深藏于水底的”看不见“东西。
当人人都在用Codex创建web应用时,有多少人了解CSRF漏洞
今天,我要回顾的就是CSRF漏洞

CSRF 简介

CSRF(Cross-Site Request Forgery,跨站请求伪造),也被称为XSRFOne-Click Attack。它是一种经典且基础的安全漏洞。

CSRF 的本质是利用浏览器自动携带 Cookie 的机制,冒用用户已登录的身份向目标网站发送伪造请求。 攻击者不需要知道用户的密码,也不需要入侵目标服务器,只需要诱导用户在已登录的状态下访问恶意页面即可。 目标网站仅凭 Cookie 验证身份,无法判断请求是否为用户真实意图,从而被欺骗执行了非授权操作。

一个经典的,教科书级别的CSRF攻击场景

你登录了银行网站 A,然后在一个论坛里点击了一个看起来无害的链接。这个链接实际上是一个构造了转账请求的<img>标签。浏览器自动附带了你的 Cookie,银行后台看到的是合法用户的请求,于是转账执行成功。

CSRF 攻击的三个必要条件

  1. 用户已登录目标网站(浏览器中保有有效的身份凭证,如 Session Cookie)。
  2. 攻击者能构造出目标网站的有效请求(请求参数可预测或可枚举)。
  3. 目标网站没有对请求来源做有效校验(如缺少 CSRF Token、SameSite 等防护机制)。

下面的四种防御思路,本质上就是分别破坏上述三个条件中的一条或多条。当其中一条被打破,CSRF攻击就无法成立。

核心防御思路

  1. Anti-CSRF Token:每个敏感请求附带一个随机、与用户会话绑定的 Token,服务端校验;破坏条件2(请求不可构造/请求变了)。
  2. SameSite Cookie:设置 Cookie 的SameSite属性为LaxStrict,阻止第三方请求携带 Cookie;破坏条件1(Cookie不自动携带)。
  3. 双重验证:操作前要求输入密码、验证码或二次确认;破坏条件3(新增校验)。
  4. Referer / Origin 校验:检查请求来源是否为可信站点;破坏条件3(校验来源)。

实战

以下实战代码来至于我的简历智能体(https://www.craftaidhub.com/resume_app/)项目。

一、核心 CSRF 防御:SameSite Cookie

文件:src/security/cookie.rs 第 64 行

Cookie::build(self.cookie_name.clone(), user_id.to_string()) .http_only(true) .secure(false) .same_site(SameSite::Strict) // ← 关键:严格同站模式 .max_age(cookie_duration) .finish()

SameSite::Strict 是当前最有效的 CSRF 防御手段之一:

  • 浏览器在来自其他站点的跨站请求(包括通过<form>提交的 POST、通过<img>发起的 GET)中,不会附带resume_agent_user_id这个 Cookie。
  • 攻击者构造伪造页面 → 用户浏览器自动发起请求 → 因 Cookie 未携带 → 中间件无法解析 user_id → 请求被拦截。

效果:当用户登录某钓鱼网站,该网站通过<form action="https://your-server/api/v1/startAnalyze" method="POST">发起跨站请求时,用户的身份 Cookie 不会被带上,因此无法冒充用户提交分析请求。

二、第二层:Cookie 签名(防篡改)

文件:src/security/cookie.rs 第 136–184 行

fn sign_cookie(&self, cookie: &mut Cookie<'_>) -> Result<(), AppError> { let mut mac = HmacSha256::new_from_slice(&self.secret_key)?; mac.update(message.as_bytes()); let signature = hex::encode(mac.finalize().into_bytes()); let signed_value = format!("{}.{}", cookie.value(), signature); cookie.set_value(signed_value); }
  • 使用 HMAC-SHA256 对 cookie_name=cookie_value 计算签名
  • 签名附加在 Cookie 值末尾,格式为 user_id.signature
  • 验证时使用 恒定时间比较(subtle::ConstantTimeEq)防止时序攻击

CSRF 关联:即使攻击者通过某种方式强行设置了 Cookie(如 XSS 或其他漏洞写入了 resume_agent_user_id),因为无法伪造 HMAC 签名,中间件在验证时会拒绝该请求。

三、第三层:请求身份验证中间件

文件:src/middleware.rs 第 69–166 行

// 在 SecurityMiddleware 中 if path.starts_with("/api/v1/") && !path.starts_with("/api/v1/status") && !path.starts_with("/api/v1/feedback/question") && !path.starts_with("/api/v1/feedback/submit") { match app_state.cookie_security .extract_user_id_from_request(&http_req, &*app_state.user_repo) .await { Ok(Some(user_id)) => { /* 继续 */ }, Ok(None) => { /* 新用户或无效用户,不设置 user_id */ }, Err(e) => { return Err(BadRequest("Invalid user session")); } } }

所有受保护的 API 接口(/api/v1/startAnalyze、/api/v1/analyze、/api/v1/diagnosis、/api/v1/resume/* 等)在进入 handler 之前,中间件会:

  1. 解析请求中的 resume_agent_user_id Cookie
  2. 验证 HMAC 签名
  3. 查 Redis 确认用户存在
  4. 验证失败 → 直接返回 400 / 401

这意味着跨站请求即使走到了中间件层,也会因为签名验证失败或用户不存在而被拒绝。

四、辅助防御:速率限制

文件:src/security/rate_limit.rs

/// 检查用户是否超过速率限制 pub fn check_user_rate_limit(&self, user_id: &str) -> Result<bool, AppError> { ... // 如果超过窗口时间,重置计数 if now.duration_since(request_info.last_request) > self.window_size { request_info.count = 0; request_info.last_request = now; } // 检查是否超过限制 if request_info.count >= self.system_limits.max_requests_per_user { warn!("Rate limit exceeded for user: {}", user_id); return Ok(false); } ... }

五、辅助防御:输入内容安全验证

文件:src/security/rate_limit.rs 第 200+ 行

fn contains_suspicious_content(input: &str) -> bool { // 扫描 <script>、<iframe>、onerror=、javascript: 等 XSS 特征 }

在 startAnalyze、diagnosis 等 POST 接口的 handler 中,对 JD 和简历内容进行安全过滤。这防止了攻击者通过 CSRF 漏洞注入恶意脚本执行 XSS。

六、辅助防御:反馈提交动态密钥

文件:src/middleware/feedback_validation.rs

impl<S, B> actix_web::dev::Service<ServiceRequest> for FeedbackValidationMiddlewareService<S> where S: actix_web::dev::Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static, S::Future: 'static, B: MessageBody + 'static, { fn call(&self, req: ServiceRequest) -> Self::Future { let app_state = self.app_state.clone(); let service = self.service.clone(); Box::pin(async move { // Only validate for feedback submission endpoint if req.path() == "/api/v1/feedback/submit" && req.method() == "POST" { // Parse the request body to get dynamic_key match req.json::<serde_json::Value>().await { Ok(json_body) => { if let Some(dynamic_key) = json_body.get("dynamic_key").and_then(|k| k.as_str()) { // Validate the dynamic key match app_state.feedback_service.validate_dynamic_key(dynamic_key).await { Ok(true) => { ... } Ok(false) => { // Key is invalid or expired ... } Err(e) => { error!("Failed to validate dynamic key: {}", e); ... } } } else { ... } } Err(e) => { ... } } } ... }) } }

/api/v1/feedback/submit 接口在 FeedbackValidationMiddleware 中验证 dynamic_key:

  • 客户端提交反馈时必须先请求 GET /api/v1/feedback/question 获取一个带有 TTL 的动态密钥
  • 提交时中间件验证该密钥
  • 密钥过期或在别处使用过都会失败

这为反馈提交接口提供了一个 CSRF token 的等效实现。

后记

我写这篇文章的目的主要是为了记录,所以结束得比较冲忙,在AI编程时代,感觉程序员的时间不是变多了,反而是变少了。
同时,参考的链接也记录了下来,也是很有价值的。这些链接,要是放在10多年前,访问量也是相当火爆的。

参考

  • 阿里云 web渗透-CSRF漏洞
  • YesWeHack 经典示例
  • Portswigger.net CRSF三条件
http://www.jsqmd.com/news/964053/

相关文章:

  • Warcraft Helper技术深度解析:让经典魔兽争霸3在现代系统重获新生的兼容性引擎
  • 一款高性能宽工作电压的XL420S接收芯片,小封装适合应用在玩具产品上
  • 美团开源 136 亿参数视频生成大模型!生成分钟级长视频不崩不糊,MIT 协议商用无忧
  • 如何突破平台限制:用yuzu模拟器在PC上畅玩Switch游戏的革命性方案
  • Protel 99 SE PCB拼板全攻略:从特殊粘贴到队列粘贴的规范操作
  • QKeyMapper深度指南:如何通过智能按键映射提升Windows操作效率
  • 从辅助工具到核心生产力:AI编程的进化之路
  • VMware macOS解锁神器:3分钟快速安装完整指南
  • 英语阅读_The Kingdom of Mali
  • Maxwell自动化避坑指南:Python调用COM接口时,这5个错误千万别犯(附解决方案)
  • Win11 X-Lite 26H1 各版本说明与完整安装技术教程
  • 6月3号
  • 点击率会影响谷歌排名吗?B2B站点CTR低于2%的急救方法
  • 快速原型开发:用快马平台一键生成基于trae状态管理的待办应用
  • 【限时解禁】CSDN AI分发撤回隐藏功能解锁:仅开放给近30天发布≥5篇AI增强内容的认证作者(附准入校验代码)
  • 微电网协调控制系统柜的分类:按场景、功率、控制模式划分
  • 当vibe coding遇见AI:用快马平台打造能理解自然语言的智能待办应用
  • 新手福音:用快马ai生成obs吸附安装包入门示例代码
  • 终极指南:Flow Launcher搜索功能失效的完整解决方案
  • 复制视频链接怎么去水印?全平台解析实操指南 - 时时资讯
  • 新手入门linux不再难,用快马生成交互式命令行学习工具
  • TLC5615 DAC驱动开发全解析:从数据手册到Proteus仿真实践
  • 别再盲目加卡片了!——20年平台生态研究者独家披露:CSDN推荐权重重算周期中,营销卡片的3次关键扣分节点
  • C#调用Bartender打印标签?试试导出为图片或PDF,实现无打印机调试与存档
  • 3分钟掌握WindowResizer:彻底解决Windows窗口调整难题
  • 【AI捐赠革命指南】:2024年全球73%非营利组织已部署的智能捐赠整合框架,你还在用Excel手动匹配?
  • CSDN会员权益再进化:AI数字营销模块含3类算法引擎、5维用户画像、实时ROI看板——你用对了吗?
  • 如何用Untrunc免费修复损坏的MP4视频:终极拯救方案
  • 2026年 厦门防撞车厂家:70K/80K/100K智能防撞缓冲车,道路防护与安全实力品牌深度解析 - 品牌企业推荐师(官方)
  • 运算放大器仿真与实战:8个Proteus模型带你从理论到设计