保护你的 Node.js 应用免受跨站请求伪造

2025-06-04

保护你的 Node.js 应用免受跨站请求伪造

《保护您的 Node.js 应用程序免受跨站点请求伪造》最初于 2018 年 1 月在Twilio 博客上发布。


跨站请求伪造(CSRF/XSRF ,简称C-Surf)是Web应用程序常见的一种典型攻击方式。攻击者利用这些攻击在应用程序中以用户的名义执行请求,而用户却浑然不知。让我们来看看他们是如何实现这一攻击的,以及我们如何保护我们的应用程序免受此类威胁。

让我们谈谈理论

装饰性 gif

在预防 CSRF 攻击之前,我们需要了解它们的工作原理。通常,这些攻击针对的是使用基于表单提交(例如POST请求)和基于 Cookie 的身份验证的 Web 应用程序的功能。

攻击者在其恶意页面中放置一个隐藏表单,该表单会自动POST向您的页面端点发出请求。然后,浏览器会自动将存储在该页面的所有 Cookie 随请求一起发送。例如,如果用户已登录当前会话,攻击者就可以在用户不知情的情况下,以登录用户的名义发送消息。攻击者无需访问页面的 Cookie 即可实现此目的。

可视化 CSRF 攻击的图表

我们可以通过使用 CSRF 令牌来保护自己免受此类攻击。其原理是,当浏览器从服务器获取页面时,它会将一个随机生成的字符串作为 CSRF 令牌以 Cookie 的形式发送。之后,当您的页面执行 POST 请求时,它会将 CSRF 令牌以 Cookie 的形式发送,或者以其他方式发送,例如通过正文中的参数或 HTTP 标头(例如X-CSRF-Token)。

可视化 CSRF 令牌保护的图表

攻击者将无法使用隐藏表单重现相同的行为,因为他们无法访问 cookie 来检索值并将其与恶意 POST 请求一起发送。

这个概念几乎可以在任何 Web 应用程序中实现,但让我们看看如何在Express应用程序中实现它。

准备好董事会

装饰性 gif

首先,我们需要一个应用程序来了解 CSRF 漏洞的实际运作方式,以及如何进行防御。如果您已经有一个Express应用程序,请按照以下步骤操作。或者,您也可以按照以下步骤设置我们的演示应用程序。

在开始之前,请确保你已经安装了 [Node.js]( https://nodejs.org ] 和npm或其他包管理器。通过在终端中运行以下命令来启动新项目:

mkdir csrf-demo
cd csrf-demo
npm init -y
npm install express body-parser --save
Enter fullscreen mode Exit fullscreen mode

接下来创建一个名为的新文件index.js并将以下代码放入其中:

const express = require('express');
const bodyParser = require('body-parser');

const PORT = process.env.PORT || 3000;
const app = express();

app.use(bodyParser.urlencoded({
  extended: true
}));

app.get('/', (req, res) => {
  res.send(`
    <h1>Hello World</h1>
    <form action="/entry" method="POST">
      <div>
        <label for="message">Enter a message</label>
        <input id="message" name="message" type="text" />
      </div>
      <input type="submit" value="Submit" />
    </form>
  `);
});

app.post('/entry', (req, res) => {
  console.log(`Message received: ${req.body.message}`);
  res.send(`Message received: ${req.body.message}`);
});

app.listen(PORT, () => {
  console.log(`Listening on http://localhost:${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

通过运行以下命令启动应用程序:

node .
Enter fullscreen mode Exit fullscreen mode

访问http://localhost:3000,您将看到Hello World下面一个小表格。

显示 hello world 和输入字段的网页屏幕截图

危险水域

当前服务器有两个端点。一个是访问http://localhost:3000/ 时显示的主页。另一个是http://localhost:3000/entryPOST上的端点。当我们填写表单并点击“提交”时,我们会向这个端点发出请求。POST

尝试在表单中输入一些文本并点击提交。你应该会看到返回的消息,并且它应该会记录到你正在运行的服务器的控制台中。

显示消息输出的终端窗口的屏幕截图

不幸的是,攻击者能够在其页面上执行相同的请求。为了模拟这种情况,我们在Glitch 的一个页面上实现了相同的表单。访问csrf-attack.glitch.me,输入消息并点击提交。其行为与在页面上提交表单相同localhost。它会传输消息以及已设置的任何 Cookie。

在这种情况下,我们创建了一个用户可以自行提交的表单,但它可能是一个隐藏的表单,会自动提交恶意内容。让我们看看如何保护我们的页面免受此类攻击。

csurf

有多个模块可以帮助你在应用程序中实现 CSRF 令牌。其中之一是csurf 。运行以下命令安装该模块以及cookie-parser依赖项:

npm install cookie-parser csurf --save
Enter fullscreen mode Exit fullscreen mode

这两个模块都是中间件,可以在 Express 中改变请求的行为。我们已经使用它body-parser来解析请求POST体以检索消息。此外,我们还将使用它来检查_csrf令牌。cookie-parser中间件将检查令牌是否在 Cookie 中可用,并通过检查令牌是否同时存在于 Cookie 和请求体中且两者匹配,csurf自动保护任何POSTPUTPATCH或操作。DELETE_csrf

将以下代码添加到您的index.js文件中以配置中间件:

const express = require('express');
const bodyParser = require('body-parser');
const csurf = require('csurf');
const cookieParser = require('cookie-parser');

const PORT = process.env.PORT || 3000;
const app = express();

const csrfMiddleware = csurf({
  cookie: true
});

app.use(bodyParser.urlencoded({
  extended: true
}));
app.use(cookieParser());
app.use(csrfMiddleware);

app.get('/', (req, res) => {
  res.send(`
    <h1>Hello World</h1>
    <form action="/entry" method="POST">
      <div>
        <label for="message">Enter a message</label>
        <input id="message" name="message" type="text" />
      </div>
      <input type="submit" value="Submit" />
      <input type="hidden" name="_csrf" value="${req.csrfToken()}" />
    </form>
  `);
});

app.post('/entry', (req, res) => {
  console.log(`Message received: ${req.body.message}`);
  res.send(`CSRF token used: ${req.body._csrf}, Message received: ${req.body.message}`);
});

app.listen(PORT, () => {
  console.log(`Listening on http://localhost:${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

重启服务器并访问http://localhost:3000。在输入框中输入一些文本,然后点击Submit。你应该会看到控制台中出现一条消息,并在浏览器中看到类似下面的欢迎消息:

显示 CSRF 令牌和已提交消息的浏览器屏幕截图

现在切换回Glitch 的演示页面并在那里输入一条消息。点击提交后,您会看到请求失败,并且该消息不会显示在控制台中。cookie_csrf已传输,但是页面在正文中发送的值POST_csrf值不同。因此,该请求被中间件阻止csurf,我们成功保护了自己免受 CSRF 攻击。

下一步是什么?

我们已经了解了如何轻松地将 CSRF 令牌集成到基于 Node.js 且代码为服务端渲染的应用程序中。然而,将 CSRF 令牌与您的前端框架和库结合使用也同样简单。由于我们将令牌作为 Cookie 发送,因此您可以轻松读取它,并将其作为标头随异步请求一起发送。事实上,AngularHttpClient已经内置了此功能

要了解如何保护 Node.js 应用程序,请务必查看我的博客文章“保护您的 Express 应用程序”。此外,您还应该查看OWASP 页面,因为它涵盖了广泛的安全相关主题。

如果您有任何问题或任何其他有用的工具来提高 Node.js Web 应用程序的安全性,请随时联系我:


《保护您的 Node.js 应用程序免受跨站点请求伪造》最初于 2018 年 1 月在Twilio 博客上发布。

文章来源:https://dev.to/dkundel/protect-your-nodejs-app-from-cross-site-request-forgery--46b9
PREV
你可能不需要 date-fns
NEXT
Vue 30 天学习 - 方法和计算属性