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