CJoy 框架 CSRF 防护实战:CsrfProtect 中间件配置、Token 生成与完整接入指南

发布时间:2026/10/14 7:51:50
CJoy 框架 CSRF 防护实战:CsrfProtect 中间件配置、Token 生成与完整接入指南 后端Web框架MCP 服务【免费下载链接】cjoy一个高性能、可扩展、轻量、省心的仓颉应用开发框架。IoCRest宏路由Json中间件参数绑定与校验文件上传下载OAuth2MCP......项目地址https://gitcode.com/Cangjie-SIG/cjoy点击查看免费下载CSRFCross-Site Request Forgery跨站请求伪造是一种诱导已认证用户在目标网站上执行非预期操作的攻击方式。CJoy 内置的CsrfProtect中间件基于 Session 存储的随机 Secret通过「SHA256 哈希 Base64 编码」生成 CSRF Token 并在请求回程中校验为表单提交、AJAX 等状态改变请求提供开箱即用的防护。本文基于 doc/usage_guide/csrf.md 与 src/middleware/csrf.cj 源码系统讲解 CSRF 攻击原理、中间件工作原理、全部配置选项、表单与 AJAX 两种接入方式及安全部署注意事项读完即可在 CJoy 应用中快速落地一套可用的 CSRF 防护方案。一、CSRF 攻击与 Token 防护机制概述1.1 什么是 CSRF 攻击CSRF 攻击的核心场景是攻击者构造一个恶意页面诱导已经登录目标网站的受害者浏览器在不知情的情况下向目标网站发起携带合法 Cookie 的请求例如转账、改密、发帖。由于浏览器会自动附带 Cookie服务器无法区分该请求是用户主动发起还是被攻击者劫持发起从而造成非预期操作。CJoy 的CsrfProtect中间件通过Synchronizer Token Pattern同步令牌模式应对此类攻击只有请求携带的 Token 与服务器端 Session 中保存的 Secret 派生出的 Token 一致时才认为请求真实有效。1.2 CJoy 中间件的核心设计CsrfProtect基于以下核心概念设计参见 src/middleware/csrf.cj 源码Token 生成基于 SHA256 哈希 Base64 编码生成 CSRF TokenSession 集成Secret 存储在 Session 中Token 与 Session 生命周期绑定确保 Token 安全灵活配置支持从 Header、Query、Form、Param 等位置获取 Token过滤函数支持跳过特定请求的 CSRF 验证自定义错误处理支持自定义验证失败响应默认返回 401。核心类为CsrfProtect它继承JoyMiddlewareHandler通过joy.router.use()挂载到全局或分组路由上。二、工作原理CsrfProtect的完整校验流程分为生成与验证两个阶段服务器生成随机 Secret在中间件首次处理请求时将构造函数传入的secret写入当前 Session对应源码中的csrfSecretCtxKey __csrf_secret__生成 CSRF Token首次需要 Token 时先从 Session 读取或新生成一个随机 Salt对应csrfSaltCtxKey __csrf_salt__由 src/utils/rand.cj 的secureRandStr(16)使用SecureRandom生成然后计算SHA256(${salt}-${secret})并将摘要字节经 Base64 URL 安全编码无填充//替换为-/_参见 src/utils/base64.cj 的rawBase64URLEncode得到 Token缓存到 Session 中csrfTokenCtxKey客户端携带 Token客户端在表单隐藏域、请求头或查询参数中携带此 Token服务器校验对需要保护的请求从指定位置提取 Token用相同的 Salt 与 Secret 重新计算并比对一致则放行否则触发错误处理。从源码实现看src/middleware/csrf.cj中间件handle的执行顺序是先执行过滤函数若命中则直接chain.next跳过验证→ 写入 Secret → 判断ignoreMethods放行无需保护的 HTTP 方法→ 检查 Session 中是否存在 Salt → 提取 Token 并比对。由于攻击者无法读取响应内容也就无法得知 Token 值伪造请求自然无法通过校验。三、基本用法与 Session 配合注册CsrfProtect必须配合 SessionManager 使用因为 Token 的 Secret 与 Salt 都存放在 Session 中。注册顺序为先注册 Session 中间件再注册 CsrfProtect 中间件。import cjoy.middleware.{SessionManager, CsrfProtect, getSession} import cjoy.middleware.JoySessionConfig import cjoy.session.MemorySessionStorage main(): Int64 { let joy Joy.default() // 1. 注册 Session 中间件CsrfProtect 依赖 Session let sessionManager SessionManager(MemorySessionStorage()) joy.router.use(sessionManager) // 2. 注册 CsrfProtect 中间件 let csrf CsrfProtect( secret: your-secret-key-here ) joy.router.use(csrf) joy.run(127.0.0.1, 8080) return 0 }关于 Session 层仓库提供了JoySessionConfig见 src/middleware/session.cj用于精细控制 Cookie 行为例如let config JoySessionConfig( cookieName: sid, // 会话 Cookie 名称默认 sid cookiePath: /, // Cookie 路径默认 / cookieSecure: true, // 生产环境建议 true仅 HTTPS 传输 cookieHttpOnly: true, // 默认 true禁止脚本访问缓解 XSS 窃取 cookieMaxAge: 3600, // Cookie 生命周期秒默认 3600 idLength: 32 // 会话 ID 长度默认 32 ) let sessionManager SessionManager(MemorySessionStorage(), config: config)其中MemorySessionStorage见 src/session/memory_session.cj默认会话最大存活时间为 1 小时并通过 src/session/session_base.cj 中的clean()周期清理过期会话——这意味着 Session 过期后其内的 CSRF Secret 与 Salt 一并失效Token 也随之失效。四、获取 CSRF Token在渲染表单、返回页面或初始化前端时使用getCsrfToken(ctx)函数src/middleware/csrf.cj生成或获取当前会话的 CSRF Token。该函数会先读取 Session 中的 Secret若 Session 中已缓存 Token 则直接复用否则生成 Salt 并计算 Token 后写入 Session 返回。其典型用法是将 Token 嵌入 HTML 表单的隐藏域import cjoy.middleware.getCsrfToken Get[path /form] func showForm(ctx: JoyContext): Unit { // 获取 CSRF Token let token getCsrfToken(ctx) // 在 HTML 表单中使用 let html html body form methodPOST action/submit input typehidden namecsrf_token value${token} / input typetext nameusername / button typesubmit提交/button /form /body /html ctx.html(html) }仓库中的官方示例 examples/src/csrf/main.cj 给出了更精简的两种用法通过Get[path /get_token]路由直接返回 Token 字符串供前端获取再在Post[path /submit_form]路由中携带 Token 提交。Token 可以放在表单、Header 等位置位置由下文lookupKey决定。五、配置选项详解CsrfProtect的构造函数支持五个参数见 src/middleware/csrf.cj参数类型默认值说明secretString-用于生成 Token 的密钥ignoreMethodsArrayString[GET, HEAD, OPTIONS]不需要 CSRF 保护的 HTTP 方法lookupKeyStringheader:X-CSRF-TOKEN从请求中提取 Token 的位置filterHandlerJoyRequestFilterFuncNone用于跳过 CSRF 验证的过滤函数errorHandlerJoyRequestHandlerFunc返回 401CSRF 验证失败时的处理函数其中默认的ignoreMethods对应框架常量 src/framework/consts.cj 中的METHOD_GET、METHOD_HEAD、METHOD_OPTIONS即默认对 GET、HEAD、OPTIONS 请求不做校验。5.1 ignoreMethods指定无需保护的 HTTP 方法// 默认不保护 GET、HEAD、OPTIONS let csrf CsrfProtect(secret: my-secret) // 只保护 POST 方法 let csrf CsrfProtect( secret: my-secret, ignoreMethods: [GET, HEAD, OPTIONS, PUT, DELETE, PATCH] )5.2 lookupKey指定 Token 提取位置lookupKey的格式为source:name支持四种来源source说明底层取值 APIheader从 HTTP 请求头获取ctx.getHeader(name)query从 URL 查询参数获取ctx.getQuery(name)form从 POST 表单获取ctx.getPostForm(name)param从 URL 路径参数获取ctx.getParam(name)// 从请求头获取默认 let csrf CsrfProtect( secret: my-secret, lookupKey: header:X-CSRF-TOKEN ) // 从表单获取 let csrf CsrfProtect( secret: my-secret, lookupKey: form:csrf_token ) // 从查询参数获取 let csrf CsrfProtect( secret: my-secret, lookupKey: query:token )从源码createExtractFuncsrc/middleware/csrf.cj可以确认lookupKey必须以:分隔为恰好两段source只能是header、query、form、param之一否则会抛出IllegalArgumentException格式或来源非法时均有明确的错误提示。提取不到对应值时返回空字符串后续校验会因「miss token」而触发错误处理。5.3 filterHandler按请求跳过验证过滤函数返回true时跳过 CSRF 验证例如公开 API、Webhook 回调等场景let csrf CsrfProtect( secret: my-secret, filterHandler: { ctx: JoyContext // 公开 API 不需要 CSRF 保护 ctx.path.startsWith(/api/public/) } )5.4 errorHandler自定义验证失败响应let csrf CsrfProtect( secret: my-secret, errorHandler: { ctx: JoyContext ctx.status(401).json(ToJson({ code: 401, message: CSRF 验证失败请刷新页面重试 })) } )若未配置errorHandler则使用内置的DefaultCsrfErrorHandlersrc/middleware/csrf.cj它通过JoyLogError记录一条包含客户端 IP、请求方法与 URL 的错误日志并返回 HTTP 401 状态码。源码中三类失败场景都会触发该处理器并各自记录日志miss saltSession 中无 Salt、miss token in source请求中未携带 Token、invalid tokenToken 不匹配这些日志可用于监控和追踪 CSRF 攻击尝试。六、完整示例表单场景以下完整示例覆盖 doc/usage_guide/csrf.md 第五节并保持可运行性演示了「GET 渲染带 Token 的表单 → POST 提交校验」的完整链路import cjoy.Joy import cjoy.middleware.{SessionManager, CsrfProtect, getSession, getCsrfToken} import cjoy.middleware.JoySessionConfig import cjoy.session.MemorySessionStorage import cjoy.json.macros.* main(): Int64 { let joy Joy.default() // 注册 Session let sessionManager SessionManager(MemorySessionStorage()) joy.router.use(sessionManager) // 注册 CsrfProtect let csrf CsrfProtect( secret: my-csrf-secret-key, lookupKey: form:csrf_token ) joy.router.use(csrf) // 显示表单GET 请求不需要 CSRF 验证 joy.router.get(/form, {ctx: JoyContext let token getCsrfToken(ctx) ctx.html( !DOCTYPE html html headtitleCSRF 保护表单/title/head body h1CSRF 保护表单示例/h1 form methodPOST action/submit input typehidden namecsrf_token value${token} / label用户名: input typetext nameusername //labelbr button typesubmit提交/button /form /body /html ) }) // 处理表单提交POST 请求需要 CSRF 验证 joy.router.post(/submit, {ctx: JoyContext if (let Some(username) - ctx.getPostForm(username)) { ctx.json(ToJson({ message: 提交成功, username: username })) } else { ctx.json(ToJson({message: 用户名不能为空})) } }) joy.run(127.0.0.1, 8080) return 0 }运行后访问http://127.0.0.1:8080/form可看到隐藏了csrf_token的表单直接向/submit发起不含 Token 的 POST 请求会得到 401 响应而携带合法 Token 的提交则会成功。七、AJAX 请求接入对于单页应用SPA或前端框架场景表单隐藏域不可用需要在请求头中携带 CSRF Token// 前端获取 Token 并存储 const token document.querySelector(input[namecsrf_token]).value; localStorage.setItem(csrfToken, token); // 发送 AJAX 请求时携带 Token fetch(/api/data, { method: POST, headers: { Content-Type: application/json, X-CSRF-TOKEN: localStorage.getItem(csrfToken) }, body: JSON.stringify({ data: value }) });服务器端配置为从请求头提取let csrf CsrfProtect( secret: my-secret, lookupKey: header:X-CSRF-TOKEN // 从请求头获取 ) joy.router.use(csrf)需要注意的是Token 与 Session 绑定若 Token 在 localStorage 中长期保存而 Session 已过期例如超过MemorySessionStorage默认的 1 小时校验会因 Session 中 Salt 缺失而失败此时应引导用户重新获取 Token。因此实践中更推荐每次进入页面时通过getCsrfToken重新获取最新 Token并在每次状态改变请求POST、PUT、DELETE、PATCH 等中携带。八、注意事项与安全实践Session 依赖CsrfProtect必须配合SessionManager使用且需先注册 Session 再注册 CSRF 中间件否则getSession(ctx).getOrThrow()会因取不到 Session 而抛出异常Secret 密钥使用强随机密钥不要在代码中硬编码建议通过配置中心或环境变量注入secret是 Token 计算的关键输入泄露后攻击者可自行推导合法 TokenToken 生命周期Token 与 Session 绑定Session 失效后 Token 也会失效Token 在同一 Session 内首次生成后即被缓存复用无需每次请求重新生成GET 请求默认 GET、HEAD、OPTIONS 请求不需要 CSRF 验证因为 CSRF 攻击通常依赖表单 POST 或 AJAX 提交等状态改变方法前端框架使用前端框架时需要将 Token 注入到每个状态改变请求中请求头、表单字段或查询参数均可注意与lookupKey保持一致HTTPS生产环境必须使用 HTTPS防止 Token 在传输中被中间人窃取SameSite Cookie配合HttpOnly和SecureCookie 使用增强安全性——JoySessionConfig默认已开启cookieHttpOnly生产环境应将cookieSecure设为true设置SameSite属性可进一步阻止跨站携带 Cookie日志记录验证失败会通过JoyLogError记录错误日志含客户端 IP、请求方法与 URL可用于监控和识别攻击行为公开接口豁免对无需防护的公开 API、Webhook 等使用filterHandler显式跳过而非放宽ignoreMethods或关闭整个中间件。九、小结CJoy 的CsrfProtect中间件以 Session 为信任根将「密钥 随机 Salt → SHA256 摘要 → Base64 URL 编码」的 Token 机制与灵活的提取位置header/query/form/param、方法白名单ignoreMethods、请求过滤filterHandler和自定义错误处理errorHandler组合在一起实现了对状态改变请求的透明防护。其核心实现集中在 src/middleware/csrf.cj配套的 Session 基础位于 src/session/ 与 src/middleware/session.cj可直接运行的示例见 examples/src/csrf/main.cj。按本文步骤完成注册与 Token 下发后即可在表单、AJAX 等多种场景下获得 CSRF 防护能力。赞分享后端Web框架MCP 服务【免费下载链接】cjoy一个高性能、可扩展、轻量、省心的仓颉应用开发框架。IoCRest宏路由Json中间件参数绑定与校验文件上传下载OAuth2MCP......项目地址https://gitcode.com/Cangjie-SIG/cjoy点击查看免费下载相关推荐Fresh 框架 CSRF 防护中间件 csrf() 实战指南基于 Sec-Fetch-Site 与 Origin 的跨站请求伪造防护Fresh 框架 CSRF 防护中间件 csrf 实战指南基于 Sec Fetch Site 与 Origin 的跨站请求伪造防护 导读 本文聚焦 Fresh后端前端Drogon框架安全防护指南HTTPS配置与CSRF防护全攻略Drogon框架安全防护指南HTTPS配置与CSRF防护全攻略 你是否还在为Web应用的安全防护而烦恼Drogon作为基于C17/20的高性能Http应后端Web框架在 Go Web 框架中集成 templChi、Echo、Gin、Fiber 实战与 Go 1.25 内置 CSRF 防护指南在 Go Web 框架中集成 templChi、Echo、Gin、Fiber 实战与 Go 1.25 内置 CSRF 防护指南 templ 是一个专为在 Go开发工具代码生成后端上一篇Langfuse 摄取事件重放S3 Replay运维指南基于 Athena 访问日志与 BullMQ 队列的失败补偿下一篇FastGPT 项目代码执行沙盒Code Sandbox架构与安全隔离实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询