news 2026/9/12 3:31:15

Remix 3 的 CSRF 防护中间件怎么配置

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Remix 3 的 CSRF 防护中间件怎么配置

Remix 3 的 CSRF 防护中间件怎么配置

【免费下载链接】remixThe fully-stacked web framework项目地址: https://gitcode.com/GitHub_Trending/re/remix

在一个基于fetch-router的 Remix 3 应用中,POSTPUTPATCHDELETE这类会改变状态的请求默认没有任何防跨站请求伪造(CSRF)的屏障。本文的目标是完成一件具体的事:在路由的中间件链里启用remix/middleware/csrf提供的csrf()中间件,让携带 session cookie 的危险方法请求必须同时通过"来源校验 + 同步化令牌比对"才能放行,并知道怎样验证配置确实生效。

仓库中 remix 包的版本 当前为3.0.0-rc.2,要求 Node>=24.3.0;安装命令是文档给出的npm i remix,CSRF、Session、Cookie 相关模块都随remix包一起提供。

前提:session 中间件必须先于 csrf 运行

csrf()的令牌存储在服务端 session 里,因此它硬性依赖session()中间件先执行。如果你把csrf()放在session()前面,或完全漏掉 session 中间件,中间件会直接抛出错误:

csrf middleware requires session() middleware to run before it

见 csrf 中间件实现。session cookie 还必须是签名的,session-middleware 的 README 明确说明"session cookies must be signed"以防止客户端篡改会话数据。

如果令牌通过隐藏表单字段提交,还需要formData()中间件把请求体解析出来,否则_csrf字段读不到——token 表单字段来源在 README 中标注了这条依赖。

配置步骤:挂载中间件并注入令牌

完整的最小配置来自 csrf-middleware README 的 Usage 示例:

import { createCookie } from 'remix/cookie' import { createRouter } from 'remix/router' import { createCookieSessionStorage } from 'remix/session-storage/cookie' import { session } from 'remix/middleware/session' import { csrf, getCsrfToken } from 'remix/middleware/csrf' let sessionCookie = createCookie('__session', { secrets: ['secret1'] }) let sessionStorage = createCookieSessionStorage() let router = createRouter({ middleware: [session(sessionCookie, sessionStorage), csrf()], }) router.get('/form', (context) => { let token = getCsrfToken(context) return new Response(` <form method="post" action="/submit"> <input type="hidden" name="_csrf" value="${token}" /> <button type="submit">Submit</button> </form> `) })

需要按自己项目替换的只有两处:secrets: ['secret1']换成你自己的签名密钥(session-middleware 的示例 用了secrets: ['s3cr3t'],并建议配置secure: true, sameSite: 'lax'),以及表单页面换成你自己的路由。getCsrfToken(context)的语义是"取 session 里的令牌,没有就创建一个",所以渲染任何含危险方法表单的页面时调用一次即可,令牌会随响应里的 session cookie 持久化。

中间件顺序方面,仓库内的 timeboxer 示例应用 展示了一个更完整的实际写法,把表单解析放在 csrf 之前:

export const router = createRouter({ middleware: [ session(sessionCookie, sessionStorage), formData(), csrf(), // ...其余业务中间件 ], })

其中 session 配置 使用环境变量SESSION_SECRET作为 cookie 签名密钥,非测试环境下缺失会直接抛错SESSION_SECRET is required——如果你的会话密钥也来自环境变量,需要保证它先于应用启动就已设置。

令牌从哪里读、往哪里传

对不安全方法(POSTPUTPATCHDELETE),csrf()按以下顺序查找提交的令牌(见 README 的 Token Sources 一节 与 实现):

  1. 请求头:X-Csrf-TokenX-Xsrf-TokenCsrf-Token(按此顺序)
  2. 表单字段:_csrf(依赖formData()中间件解析请求体)
  3. 查询参数:_csrf

文档给出的立场很明确:请求头和隐藏表单字段是推荐传输方式;查询参数仅为兼容性保留,因为令牌更容易出现在日志、历史和被复制的 URL 里,是最弱的选项。文档还提示"不要把它作为默认推荐"。

GETHEADOPTIONS默认属于安全方法,不做 CSRF 校验,会直接放行;这一行为由safeMethods选项控制。

来源校验与可选配置项

csrf()在令牌比对之外还做来源校验。默认行为:当请求带OriginReferer头时,按同源校验;allowMissingOrigin默认为true,即两个来源头都缺失时请求仍然通过(只要令牌有效)。

CsrfOptions 接口 支持的配置项:

选项默认值用途
tokenKey'_csrf'session 中存放令牌的键
fieldName'_csrf'读取令牌的表单字段名
headerNames['X-Csrf-Token', 'X-Xsrf-Token', 'Csrf-Token']依次检查的头部名
safeMethods['GET', 'HEAD', 'OPTIONS']不校验的 HTTP 方法
origin同源校验允许的跨域来源,可为字符串、正则、数组或回调函数
allowMissingOrigintrue是否放行缺少Origin/Referer的请求
value默认查找逻辑自定义令牌提取函数
onError内置 403 响应自定义拒绝响应

如果你的部署希望强制要求危险请求必须携带来源头,把allowMissingOrigin设为false;如果表单会来自另一个已知域名,通过origin传入该域名、匹配正则或数组。

如何验证配置生效

仓库的 timeboxer 安全测试 就是官方给出的验证方式,它覆盖了"拒绝"和"放行"两个方向,可以直接照此手工验证:

应被拒绝的请求——带合法 session cookie 但不带令牌或带错误令牌的POST/PUT/DELETE,响应应为403,且响应体精确等于(见测试中的断言 assertCsrfRejection):

Forbidden: missing CSRF token

Forbidden: invalid CSRF token

来源非法时的响应体是Forbidden: invalid CSRF origin(见 实现中的默认错误响应),以上三段文案在未配置onError时固定如此。

应被放行的请求——先从页面里提取渲染出的<input name="_csrf">令牌和 session cookie,再带着该令牌提交:测试里注册接口带有效令牌返回303,创建接口返回201

一个可用的手工流程(等价于测试中的做法):

  1. GET一个会渲染_csrf隐藏字段的页面,从 HTML 中提取name="_csrf"value,并记录响应Set-Cookie中的 session cookie;
  2. 用该 cookie 向同一动作地址发POST,不带_csrf字段,预期得到403Forbidden: missing CSRF token
  3. 再发一次并带上_csrf字段(或X-Csrf-Token头),预期进入正常业务逻辑(测试中的成功状态码303/201取决于路由自身,不代表固定值)。

边界与限制

  • 文档强调:同步化令牌才是csrf()的主防线,Origin/Referer检查只是附加信号,不能当作唯一保护。
  • 如果你的部署能稳定依赖Sec-Fetch-SiteOrigin头,cop()(cop-middleware)提供的无令牌跨源防护可能是更合适的选择,也可以在csrf()之前叠加一层cop()做提前拦截。README 的原话是"如果部署能保证无令牌模型的全部前提,这个中间件是可选的"。
  • 令牌比对使用恒定时间比较实现(constantTimeEqual),避免时序侧信道;令牌由crypto.getRandomValues生成 32 字节随机数。
  • 更上层的防护思路(cookie 签名密钥轮换、session 生命周期管理、CORS 不等于 CSRF 防护)见 docs/guides 的 Auth, Sessions, and Security 章节,其中明确建议"session 中间件先于csrf()运行,表单解析先于_csrf令牌提取"。

【免费下载链接】remixThe fully-stacked web framework项目地址: https://gitcode.com/GitHub_Trending/re/remix

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 3:31:01

HLS-FPGA DCNN加速器:从计算图到硬件流水线的映射原理

简介&#xff1a;本资源面向FPGA开发工程师、AI硬件加速研究者及深度学习系统优化方向的研究生&#xff0c;聚焦于解决DCNN在FPGA平台部署中开发周期长、HDL编码复杂、性能调优难等核心痛点。压缩包共2000个文件&#xff0c;总大小222.28MB&#xff0c;涵盖307个Verilog/VHDL&a…

作者头像 李华
网站建设 2026/9/12 3:30:56

2026年日志分析工具选型:从ELK到Loki与ClickHouse的实践对比

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 3:28:19

大数据选题推荐

基于Hadoop的通辽租房数据分析平台的设计与实现 基于大数据的民宿数据统计与可视化 基于Python的美妆销售系统分析与实现 智能垃圾分类系统 基于Python办公数据的简易清洗与可视化系统 电商网站婴儿商品销售数据分析系统的设计与实现 天津市空气质量多因素预测与可视化分…

作者头像 李华
网站建设 2026/9/12 3:28:10

SpringBoot+Vue构建高校毕业设计选题管理系统

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 3:26:54

职场小说创作技巧:试探、录音与失眠夜的心理描写

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华