在后端开发方面,Node.js 是开发人员最喜欢的技术之一。它的受欢迎程度不断上升,现已成为在线攻击的主要目标之一。这就是为什么保护 Node.js 免受漏洞和威胁至关重要。
在本指南中,您将看到为生产设计安全 Node.js 应用程序架构的 15 种最佳实践。实施所有这些实践,让您的后端比以往任何时候都更安全!
文章目录
- 为什么应该构建安全的 Node.js 应用程序?
- 让 Node.js 应用程序更安全的 15 个最佳实践
- 1.切勿使用 Root 权限运行 Node.js
- 2.让你的 NPM 库保持最新
- 3.避免使用默认的 Cookie 名称
- 4.设置安全 HTTP 标头
- 5.实施速率限制
- 6.确保强大的身份验证策略
- 7.不要发送不必要的信息
- 8.监控你的后端
- 9.采用仅 HTTPS 的策略
- 10.验证用户输入
- 11.使用安全检查器
- 12.防止 SQL 注入
- 13.限制请求大小
- 14.通过自动化工具检测漏洞
- 15.让漏洞报告更简单
- 结论
为什么应该构建安全的 Node.js 应用程序?
构建安全的 Node.js 应用程序非常重要,至少有以下三个原因:
- 保护用户数据:您的应用程序可能会处理敏感的用户信息,如个人信息、登录凭证、支付数据或机密的业务见解。如果不能保护这些数据,您可能会被隐私监管机构处以数百万美元的罚款。通过实施强大的安全措施,您可以保护用户数据并避免法律问题。
- 保护应用程序功能:安全漏洞可能会损害后端提供的功能。攻击者可能会利用弱点来改变您的服务、操纵数据或注入恶意代码。通过保护您的应用程序,您可以避免这种情况,并为您的用户提供无缝的体验。
- 维护声誉:安全事件可能会严重损害您的声誉并削弱对您服务的信任。客户和用户希望他们的数据得到安全处理,并且您的应用程序能够按预期运行。违规可能会导致失去信任,但通过优先考虑安全性,您可以表明您对质量的承诺。
您可能认为安全问题并没有那么普遍,遵循编码和架构最佳实践就足够了,但事实并非如此。 Node.js 的强大之处在于 NPM 环境,它提供了数百万个库。问题是大多数 NPM 包都存在一些安全漏洞。
换句话说,如果 Node.js 项目的依赖项不安全,那么它就不安全。考虑到使用外部库的流行程度,这是令人担忧的。根据 Snyk 开源安全状况报告,平均一个 Node.js 项目在 79 个直接依赖项中存在 49 个漏洞。这一相关统计数据强调了保护 Node.js 应用程序的重要性。
让 Node.js 应用程序更安全的 15 个最佳实践
1.切勿使用 Root 权限运行 Node.js
不建议使用 root 权限运行 Node.js,因为这违反了最小权限原则。无论您的后端是在专用服务器还是 Docker 容器上,您都应该始终以非 root 用户身份启动它。
如果您使用 root 权限运行 Node.js,则项目或其依赖项中的任何漏洞都可能被利用来获得对系统的未经授权的访问。例如,攻击者可以利用它们执行任意代码、访问敏感文件,甚至控制整个机器。因此,必须避免使用 Node.js 的 root 用户。
这里的最佳实践是创建一个专门的用户来运行 Node.js。该用户应仅具有启动应用程序所需的权限。这样,成功破坏您后端的攻击者将被限制为该用户的权限,从而限制了他们可能造成的潜在损害。
2.让你的 NPM 库保持最新
NPM 库使构建功能齐全的 Node.js 后端变得更容易、更快捷。同时,它们还会给您的应用程序带来安全风险。新的漏洞一直在被发现,维护人员的工作就是解决这些漏洞并发布软件包的更新版本。这就是为什么您应该保持依赖项最新的原因。
为了确保您所依赖的 NPM 库的安全,您可以使用 npm audit 和 snyk 。这些工具分析项目的依赖关系树并提供对任何已知漏洞的见解。
这是运行 npm audit
的示例:
npm audit
...
found 4 vulnerabilities (2 low, 2 moderate)
run `npm audit fix` to fix them, or `npm audit` for details
此命令利用 GitHub Advisory 数据库并检查您的 package.json
和 package-lock.json
是否存在已知安全问题。
此外,您可以使用 Snyk 检查您对 Snyk 开源漏洞数据库的依赖关系。安装 snyk
:
npm install -g snyk
在项目的根文件夹中,使用以下命令测试您的应用程序:
snyk test
要打开一个向导来引导您完成修补所发现的漏洞的过程,请运行:
snyk wizard
使用 snyk
并定期运行 npm audit
可帮助您在 NPM 库中的安全问题成为问题之前识别并修复它们。请记住,应用程序的安全性取决于依赖项中最薄弱的环节。
3.避免使用默认的 Cookie 名称
Node.js 应用程序使用的 cookie 名称可能会无意中泄露您的后端所基于的技术堆栈。这是有价值的信息,您应该始终隐藏它,因为攻击者可以再次使用它。通过了解您正在使用的框架,他们可以利用与其相关的特定弱点。
详细而言,攻击者往往将注意力集中在会话 cookie 的名称上。通过使用 express-session 中间件设置自定义会话 cookie 名称来保护您的应用程序免受此类影响:
const express = require("express");
const session = require("express-session");
const app = express();
app.use(
session({
// set a custom name for the session cookie
name: "myCustomCookieName",
// a secure secret key for session encryption
secret: "mySecretKey",
})
);
4.设置安全 HTTP 标头
Express 中默认的 HTTP 标头不是很安全。我们使用在线 Security Headers 项目检查标头安全性:
其中一些标头包含不应公开的信息,如 X-Powered-By。还有一些则是缺失的,应该添加进来,以处理各种与安全相关的问题,包括防止跨站脚本 (XSS) 攻击。
这就是 helmet 发挥作用的地方!该库负责根据安全标头的建议设置最重要的安全标头。使用方法如下:
const express = require("express");
const helmet = require("helmet");
const app = express();
// register the helmet middleware
// to set the security headers
app.use(helmet());
helmet()
中间件会自动删除不安全的标头并添加新的标头,包括 X-XSS-Protection
、 X-Content-Type-Options
、 Strict-Transport-Security
和 X-Frame-Options
。这些都是最佳实践,有助于保护您的应用程序免受常见攻击。
Node.js 应用程序设置的标头现在将被视为安全:
5.实施速率限制
DDoS(分布式拒绝服务)和暴力破解是两种最常见的 Web 攻击。为了缓解它们,您可以实施速率限制。此技术涉及控制 Node.js 后端的传入流量,防止恶意行为者用过多的请求淹没您的服务器。
实施速率限制的最简单方法是通过 rate-limiter-flexible 库。该依赖库提供了一个可配置的中间件,用于限制在指定时间内来自同一 IP 地址或用户的请求数量。
以下是如何使用它在 Node.js 中应用速率限制的示例:
const express = require("express");
const {
RateLimiterMemory,
} = require("rate-limiter-flexible");
const app = express();
const rateLimiter = new RateLimiterMemory({
points: 10, // 允许的最大请求数
duration: 1, // 时间范围,以秒为单位
});
const rateLimiterMiddleware = (req, res, next) => {
rateLimiter
.consume(req.ip)
.then(() => {
// 请求被允许,继续处理请求
next();
})
.catch(() => {
//超过请求限制,使用适当的错误消息进行响应
res.status(429).send("Too Many Requests");
});
};
app.use(rateLimiterMiddleware);
首先,初始化一个速率限制器实例,允许 1 秒内最多 10 个请求。然后,在自定义中间件中使用它来分析传入请求的 IP。如果未超出速率限制,则请求将继续。否则,请求将被阻止并且服务器返回 429 Too Many Requests
响应。
6.确保强大的身份验证策略
为了保护您的 Node.js 应用程序免受利用用户身份验证的攻击,您需要实施强身份验证策略。首先,邀请用户设置强密码。此外,您还应该支持多重身份验证 (MFA) 和单点登录 (SSO)。 MFA 通过要求用户提供多种形式的身份验证来增加额外的安全层,而 SSO 则简化了身份验证过程并降低了弱密码或重复使用密码的风险。
当需要对密码进行散列存储时,应首选 bcrypt 等强大的加密函数,而不是 Node.js crypto 库提供的方法。该软件包提供了一种安全的密码散列算法,大大增加了攻击者破解密码的难度。最后,如前所述,通过限制登录失败次数来减少暴力破解攻击。
7.不要发送不必要的信息
无意中提供给攻击者的任何信息都可能被用来对付你。因此,服务器响应只应包含调用者严格需要的内容。例如,避免直接向客户端返回详细的错误信息或堆栈跟踪。取而代之的是,提供不透露具体实现细节的通用错误信息。最简单的方法是在生产模式下运行 Node.js,设置 NODE_ENV=production env
,否则 Express 会在错误响应中添加堆栈跟踪。
同样,您必须小心 API 响应中包含的数据。仅返回必要的数据字段,并避免暴露调用者未请求的敏感信息。这将最大限度地减少意外泄露机密或特权信息的风险。
8.监控你的后端
您在生产中的后端可能正在遭受攻击,而您可能根本没有意识到这一点。这就是监控 Node.js 应用程序至关重要的原因。通过将其连接到应用程序性能监控 (APM) 工具,您可以对其进行跟踪,以发现安全问题并确保其整体健康。
幸运的是,有几个可用于 Node.js 的 APM 库和服务。其中最受欢迎的一些是 SigNoz、Sentry、Prometheus、New Relic 和 Elastic,它们提供了应用程序各个方面的信息,包括性能、错误率、资源使用情况和安全相关指标。特别是,它们可以实现实时数据收集和检测可能表明安全漏洞的异常或可疑活动。其中一些还提供可维护性功能,以跟踪 CI/CD 管道中的部署工作流程。
9.采用仅 HTTPS 的策略
通过确保只能通过 HTTPS 访问后端,您将提高客户端和 Node.js 服务器之间交换数据的机密性。 HTTPS 建立一个加密通道,保护密码、会话令牌和用户数据等敏感信息免遭拦截。
作为此类政策的一部分,您还应该使用 HTTPS cookie。为此,请确保 Node.js 应用程序设置的任何 cookie 都标记为 secure
和 httpOnly
:
res.cookie("myCookie", "cookieValue", {
// create an HTTPS cookie
secure: true,
httpOnly: true,
});
意外方或脚本将无法再访问您的 cookie。此外,它们只能通过 HTTPS 连接传输。
10.验证用户输入
每当用户有机会输入内容时,攻击者就可以利用它向服务器发送恶意数据。因此,验证用户输入对于确保 Node.js 应用程序的安全性和完整性至关重要。
11.使用安全检查器
安全检查器会分析您的代码库,以识别漏洞、不安全的代码部分和最佳实践违规行为。其中最流行的是 eslint-plugin-security ,这是一组用于在 Node.js 中实施安全开发的 ESLint 规则。
通过将此类工具集成到您的开发工作流程中,您可以及早发现并解决安全问题。具体来说,它们可以降低编码时将漏洞引入应用程序的风险。这些工具在集成到 CI/CD 管道中时特别有效。
12.防止 SQL 注入
SQL 注入是一种常见的安全漏洞,当攻击者可以操纵传递到 SQL 查询的输入数据时就会发生这种漏洞。当将用户输入直接连接到 SQL 查询中时,通常会发生这种情况。在这种情况下,攻击者可以伪造旨在执行任意 SQL 代码的特定输入,从而导致未经授权的访问和数据泄露。
有多种方法可以防止 Node.js 中的 SQL 注入。最流行的是:
- 使用准备好的语句或参数化查询:这些技术涉及将 SQL 代码与用户输入分离,防止其被解释为查询的一部分。
- 输入清理:验证用户输入以拒绝恶意数据,降低 SQL 注入攻击的风险。
- 使用 ORM:像 Sequelize 这样的 ORM 技术通常提供针对 SQL 注入的内置保护。
13.限制请求大小
Node.js 中的默认请求正文大小限制为 5 MB。为了保护您的后端免受恶意用户试图用数据淹没您的服务器的 DDoS 攻击,建议减少该限制。为此,您可以使用 body-parser 库并按如下方式配置它:
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
// 将请求大小限制设置为1 MB
app.use(bodyParser.json({ limit: "1mb" }));
请根据您的具体要求调整该值。现在,正文大于 1 MB 的请求将立即被阻止,服务器将无法分配资源来处理这些请求。
14.通过自动化工具检测漏洞
自动漏洞扫描工具(例如 SonarQube 或类似工具)是识别 Node.js 应用程序中安全问题的宝贵资源。这些工具对代码库、依赖项、配置和其他组件执行全面扫描,以识别安全漏洞。
以下是使用自动漏洞扫描器的一些主要好处:
- 早期检测:在部署应用程序之前主动识别安全问题。
- 增加覆盖范围:对所有项目文件进行深度扫描,确保高安全覆盖范围。
- 持续监控:将它们集成到 CI/CD 管道中,以确保及时发现通过代码更改引入的任何新漏洞。
15.让漏洞报告更简单
让用户和安全研究人员能够报告 Node.js 后端中发现的漏洞是确保应用程序安全的另一个重要方面。这不仅应该是可能的,而且程序必须清晰且易于理解。
security.txt 提议的标准是让研究人员与您联系的有效方法。这是一个放置在项目根目录下的简单文本文件,提供有关如何报告安全漏洞的信息。它遵循标准化格式,包括联系方式、加密方法和披露指南。
以下是基本 security.txt
文件的示例:
Contact: email@example.com
Encryption: https://example.com/pgp-key.asc
Contact
指定应向其报告安全漏洞或问题的电子邮件地址。这些电子邮件可能包含关键信息,不应公开访问。因此, Encryption
字段指示组织的 PGP 公钥的位置,该公钥可用于加密电子邮件内的消息。这种机制确保只有组织才能使用私钥解密这些消息并读取它们。
同样,您也可以考虑在网站上添加“报告漏洞”页面。
结论
在本指南中,我们讨论了在生产中保护 Node.js 应用程序安全的方法。作为开发人员,我们有责任保护用户数据、维护应用程序功能并保护我们的声誉。
通过集成这些技术、方法和技巧,您可以创建更可靠的 Node.js 架构,降低风险并确保应用程序的安全性。
原文:https://semaphoreci.medium.com/best-practices-for-securing-node-js-applications-in-production-d24b7c4981d