导读:本期聚焦于行者创作的《Node.js如何实现CSRF防护?csurf中间件与双提交Cookie模式详解》,敬请观看详情。CSRF攻击是Web应用中常见的安全威胁,攻击者诱导已登录用户在不知情的情况下执行恶意请求。本文围绕Node.js环境下的CSRF防护方案展开,重点讲解csurf中间件的配置方法、工作原理以及它被废弃后的替代方案,并深入分析双提交Cookie模式的实现细节,包括Token生成、Cookie设置、请求校验的完整流程,同时对比两种方案的适用场景与优缺点,帮助开发者在Express项目中构建可靠的CSRF防御体系。

CSRF(跨站请求伪造)是一种利用浏览器信任机制发起攻击的安全漏洞。用户登录某个网站后,浏览器会自动携带Cookie,攻击者只需诱导用户访问恶意页面,就能以用户身份发送伪造请求。在Node.js的Express项目中,如何正确地防御CSRF攻击是每个开发者必须掌握的技能。本文将详细讲解csurf中间件的使用方式,以及更加灵活的双提交Cookie模式的完整实现。

Node.js如何实现CSRF防护?csurf中间件与双提交Cookie模式详解

一、什么是CSRF攻击以及为什么需要防护

CSRF攻击的核心在于浏览器的同源策略只限制脚本读取响应,却不限制跨站请求本身携带Cookie。假设用户已经登录了银行网站bank.ippipp.com,此时浏览器保存了该网站的会话Cookie。用户随后访问了一个恶意论坛页面,该页面中嵌入了这样一段HTML代码:

<form action="https://bank.ippipp.com/transfer" method="POST">
  <input type="hidden" name="to" value="attacker"/>
  <input type="hidden" name="amount" value="10000"/>
</form>
<script>document.forms[0].submit();</script>

当页面加载完成,表单会自动提交。由于请求发往bank.ippipp.com,浏览器会自动附带该域名下的会话Cookie,服务端无法区分这个请求是用户主动发起还是被恶意页面伪造的。如果服务端没有额外的校验机制,转账操作就会被执行。

防御CSRF的思路主要有三种:校验Referer头、使用Synchronizer Token模式、以及双提交Cookie模式。其中Referer校验依赖浏览器行为,可靠性不足;Token校验是目前的主流方案,也是csurf中间件采用的方式;而双提交Cookie模式实现简单,特别适合前后端分离的架构,后面会详细展开。

二、csurf中间件的配置与使用

csurf是Express生态中曾经广泛使用的CSRF防护中间件,它的原理是生成一个随机Token,将其存入Session(或Cookie),并在渲染表单时将Token输出到页面中。表单提交时,服务端比对请求中的Token与存储的Token是否一致,不一致则拒绝请求。

使用csurf需要先安装依赖,典型的安装命令如下:

npm install express csurf cookie-parser express-session

接着在Express应用中进行配置。下面的代码展示了完整的整合方式,包括Session初始化、cookie解析以及csurf中间件的挂载:

const express = require('express');
const session = require('express-session');
const cookieParser = require('cookie-parser');
const csrf = require('csurf');

const app = express();

app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(session({
  secret: 'your-secret-key',
  resave: false,
  saveUninitialized: false
}));

// 使用Session存储Token的CSRF中间件
const csrfProtection = csrf({ cookie: false });

app.get('/form', csrfProtection, (req, res) => {
  // 将Token传给模板渲染
  res.render('form', { csrfToken: req.csrfToken() });
});

app.post('/process', csrfProtection, (req, res) => {
  res.send('操作成功');
});

// CSRF校验失败时的错误处理
app.use((err, req, res, next) => {
  if (err.code === 'EBADCSRFTOKEN') {
    return res.status(403).send('CSRF Token校验失败');
  }
  next(err);
});

app.listen(3000);

在前端模板中,需要将Token嵌入到表单的隐藏字段里,csurf默认从_csrf字段或x-csrf-token请求头中读取Token:

<form action="/process" method="POST">
  <input type="hidden" name="_csrf" value="<%= csrfToken %>"/>
  <input type="text" name="username"/>
  <button type="submit">提交</button>
</form>

需要注意的是,csurf这个包已经被官方标记为废弃,不再维护。官方推荐的替代方案是使用csrf-csrf包,它的API设计与csurf类似,但采用了双提交Cookie模式作为默认实现,同时提供了更完善的安全配置项。如果是新项目,建议直接选择后者。

三、双提交Cookie模式的原理与实现

双提交Cookie模式不需要在服务端存储任何状态。服务端生成一个随机Token后,通过Cookie发送给浏览器,同时要求后续的每个请求都在参数或请求头中携带同样的Token。服务端只需比对Cookie中的Token与请求中的Token是否一致即可。由于第三方站点无法读取目标域名的Cookie,也就无法在伪造请求中附带正确的Token,攻击自然无法得逞。

下面是一个不依赖任何第三方库、基于crypto模块手动实现双提交Cookie的例子,代码逻辑清晰且便于理解内部机制:

const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.json());

// 生成Token并写入Cookie
function issueToken(req, res, next) {
  if (!req.headers.cookie || !req.headers.cookie.includes('csrfToken=')) {
    const token = crypto.randomBytes(32).toString('hex');
    // httpOnly必须为false,否则前端JS读不到Token
    res.cookie('csrfToken', token, {
      httpOnly: false,
      sameSite: 'strict',
      secure: true
    });
    // 挂到响应局部变量,方便前端获取
    res.locals.csrfToken = token;
  }
  next();
}

// 校验Cookie与请求头中的Token是否一致
function verifyToken(req, res, next) {
  const cookieToken = req.cookies ? req.cookies.csrfToken : null;
  const headerToken = req.headers['x-csrf-token'];
  if (!cookieToken || !headerToken || cookieToken !== headerToken) {
    return res.status(403).json({ error: 'CSRF校验失败' });
  }
  next();
}

app.use(issueToken);

app.post('/api/data', verifyToken, (req, res) => {
  res.json({ message: '数据提交成功' });
});

app.listen(3000);

前端配合的方式是,在页面加载或首次请求后,从Cookie中读取Token,并在后续的AJAX请求中通过自定义请求头发送。使用fetch的示例如下:

// 从Cookie中读取csrfToken
function getCookie(name) {
  const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
  return match ? match[2] : null;
}

fetch('/api/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': getCookie('csrfToken')
  },
  body: JSON.stringify({ foo: 'bar' })
});

四、两种方案的对比与选型建议

csurf代表的Synchronizer Token模式将Token存储在服务端Session中,安全性更高,因为攻击者即使拿到Cookie中的会话标识,也无法伪造出正确的Token。但它要求应用维护Session状态,对无状态API服务或者分布式部署场景不太友好,因为多台服务器之间需要共享Session存储。

双提交Cookie模式的优势在于完全无状态,服务端不需要存储任何数据,天然适合微服务架构和水平扩展。它的安全性依赖于攻击者无法读取跨域Cookie这一前提,只要Token的生成足够随机(建议至少128位熵值),且Cookie设置了合理的sameSite属性,防护效果是有保障的。需要注意的一点是,该模式下Cookie不能设置httpOnly,否则前端脚本无法读取Token,这也意味着如果应用存在XSS漏洞,Token可能被窃取。因此CSRF防护绝不能替代XSS防护,两者需要同时做好。

实际选型时可以这样考虑:传统的服务端渲染项目,使用csrf-csrf包配合模板引擎嵌入Token即可;前后端分离的SPA应用或纯API服务,双提交Cookie模式配合Axios拦截器自动注入请求头,是最简洁高效的方案。此外,无论采用哪种方式,都建议给Cookie设置SameSite=LaxStrict属性,并在敏感操作上要求二次验证,形成多层防御。安全从来不是单一措施能解决的,纵深防御才是正确的思路。

Node.jscsurf中间件CSRF防护双提交Cookie修改时间:2026-09-04 01:06:52

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260904/49918.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。