首页
Preview

我们正遭受攻击!23个Node.js安全最佳实践

我们受到攻击了!23+ Node.js 安全最佳实践

收集、整理和编写: Yoni GoldbergKyle Martin 和 Bruno Scheufler

技术审核: Liran Tal(Node.js 安全工作组)

欢迎来到我们全面的 Node.js 安全最佳实践列表,该列表总结和整理了排名靠前的文章和博客帖子

开始前的几句话

随着安全问题成为舞台的焦点,Web 攻击也越来越猖獗。我们从全球排名靠前的文章中整理了超过 23 个 Node.js 安全最佳实践(+40 个通用安全实践)。这项工作是我们的Node.js 最佳实践 GitHub 存储库的一部分,其中包含超过 80 个 Node.js 最佳实践。注意: 许多条目都有一个“阅读更多”的链接,链接到关于该主题的详细阐述,包括代码示例和其他有用信息。

通过我们的 Twitter 账户每周获取最佳实践

1. 接受 linter 安全规则

简介: 利用与安全相关的 linter 插件(如 eslint-plugin-security)尽早捕获安全漏洞和问题,即在编码时捕获。这可以帮助捕获像使用 eval、调用子进程或导入非字符串文字的模块(例如用户输入)等安全弱点。单击下面的“阅读更多”可以查看将被安全 linter 捕获的代码示例。

否则: 在开发过程中本应简单处理的安全弱点会成为生产中的主要问题。此外,项目可能不遵循一致的代码安全实践,导致漏洞被引入或敏感密钥被提交到远程存储库。

阅读更多: Linter 规则

linting 不必只是强制执行关于空格、分号或 eval 语句的古板规则的工具。ESLint 提供了一个强大的框架,用于消除代码中各种潜在的危险模式(正则表达式、输入验证等)。我认为它提供了一种强大的新工具,值得安全意识的 JavaScript 开发人员考虑。(Adam Baldwin

更多引用和代码示例请点击此处

2. 使用中间件限制并发请求

简介: DoS 攻击非常流行且相对容易实施。使用外部服务(如云负载均衡器、云防火墙、nginx、rate-limiter-flexible 包)或(对于较小且不太关键的应用程序)速率限制中间件(例如 express-rate-limit)实现速率限制。

否则: 应用程序可能会受到攻击,导致拒绝服务,使真正的用户收到降级或无法使用的服务。

阅读更多: 实现速率限制

3. 从配置文件中提取秘密或使用包进行加密

简介: 永远不要在配置文件或源代码中存储明文秘密。相反,使用 Vault 产品、Kubernetes/Docker Secrets 或使用环境变量等秘密管理系统。最后,存储在源代码控制中的秘密必须加密并进行管理(滚动密钥、过期、审计等)。利用 pre-commit/push 钩子可以防止意外提交秘密。

否则: 即使是私有存储库,源代码控制也可能被错误地公开,此时所有秘密都会被暴露。对于外部方的源代码控制访问,将无意中提供访问相关系统(数据库、API、服务等)的权限。

阅读更多: 秘密管理# 4. 使用ORM/ODM库防止查询注入漏洞

TL;DR: 为了防止SQL/NoSQL注入和其他恶意攻击,始终使用ORM/ODM或数据库库来转义数据或支持命名或索引化参数化查询,并负责验证用户输入的期望类型。永远不要只使用JavaScript模板字符串或字符串连接将值注入查询中,因为这会为你的应用程序打开广泛的漏洞。所有知名的Node.js数据访问库(例如SequelizeKnex,mongoose)都具有内置的防注入攻击保护。

否则: 在使用MongoDB进行NoSQL工作时,未经验证或未经净化的用户输入可能导致运算符注入,而不使用适当的净化系统或ORM将很容易允许SQL注入攻击,从而创建巨大的漏洞。

**阅读更多: ** 使用ORM/ODM库防止查询注入

感谢你的努力?请在GitHub上为我们的项目点赞

5. 明确设置进程何时崩溃以避免DOS攻击

TL;DR: 当未处理错误时,Node进程将崩溃。许多最佳实践甚至建议即使捕获并处理了错误,也应退出。例如,Express将在任何异步错误时崩溃-除非你使用catch子句包装路由。这为攻击者打开了一个非常甜蜜的攻击点,他们可以识别使进程崩溃并重复发送相同请求的输入。这没有即时的治疗方法,但可以采用一些技术来缓解疼痛:每当由于未处理的错误而导致进程崩溃时,使用关键严重性进行警报,验证输入并避免由于无效用户输入而导致进程崩溃,用catch包装所有路由并考虑不在请求中崩溃时(与全局发生的情况相反)。

否则: 这只是一个合理的猜测:鉴于许多Node.js应用程序,如果我们尝试将一个空的JSON正文传递给所有POST请求,那么会有一些应用程序崩溃。此时,我们只需重复发送相同的请求即可轻松摧毁应用程序。

6. 调整HTTP响应标头以增强安全性

TL;DR: 你的应用程序应该使用安全标头来防止攻击者使用常见攻击,如跨站点脚本(XSS),点击劫持和其他恶意攻击。可以使用像helmet这样的模块轻松配置这些标头。

否则: 攻击者可能会对你的应用程序的用户执行直接攻击,从而导致巨大的安全漏洞。

阅读更多: 在应用程序中使用安全标头

7. 不断自动检查易受攻击的依赖项

TL;DR: 在npm生态系统中,一个项目通常会有许多依赖项。随着发现新的漏洞,依赖项应始终得到检查。使用npm auditnspsnyk等工具来跟踪、监视和修补易受攻击的依赖项。将这些工具与你的CI设置集成,以便在其到达生产之前捕获易受攻击的依赖项。

否则: 攻击者可以检测到你的Web框架并攻击其所有已知漏洞。

阅读更多: 依赖项安全性

8. 避免使用Node.js密码库来处理密码,使用Bcrypt

TL;DR: 应使用安全哈希+盐函数(例如bcrypt)存储密码或秘密(API密钥),由于性能和安全原因,这应该是优选的选择,而不是其JavaScript实现。

否则: 持久化密码或秘密而不使用安全函数的会受到暴力破解和字典攻击的威胁,最终会导致其泄露。

阅读更多: 使用Bcrypt# 9. 转义 HTML、JS 和 CSS 输出

TL;DR: 不可信的数据被发送到浏览器时可能会被执行,而不仅仅是被显示,这通常被称为跨站脚本攻击(XSS)。通过使用专用库将数据明确标记为永远不会被执行的纯内容(即编码、转义)来减轻此问题。

否则: 攻击者可能会将恶意 JavaScript 代码存储在你的数据库中,然后将其原样发送给客户端。

阅读更多: 转义输出

10. 验证传入的 JSON 模式

TL;DR: 验证传入请求的 body 负载并确保它符合预期,如果不符合则快速失败。为避免在每个路由中进行繁琐的验证编码,可以使用轻量级基于 JSON 的验证模式,如 jsonschemajoi

否则: 你的慷慨和宽容的方法极大地增加了攻击面,并鼓励攻击者尝试许多输入,直到他们找到一些组合来使应用程序崩溃。

阅读更多: 验证传入的 JSON 模式

11. 支持 JWT 令牌的黑名单

TL;DR: 当使用 JWT 令牌(例如,使用 Passport.js)时,默认情况下没有撤销已发出令牌的机制。一旦发现某些恶意用户活动,就没有办法阻止他们在持有有效令牌的情况下访问系统。通过实现一个不受信任的令牌黑名单来减轻此问题,每个请求都会被验证。

否则: 过期或错放的令牌可能被第三方恶意使用来访问应用程序并冒充令牌的所有者。

阅读更多: JWT 黑名单

12. 防止授权的暴力破解攻击

TL;DR: 一个简单而强大的技术是使用两个指标限制授权尝试:

  • 第一个是同一用户唯一 ID/名称和 IP 地址的连续失败尝试次数。
  • 第二个是在一段长时间内从一个 IP 地址进行的失败尝试次数。例如,如果一个 IP 地址在一天内进行了 100 次失败尝试,则阻止该 IP 地址。

否则: 攻击者可以发出无限的自动密码尝试,以获取对应用程序上特权帐户的访问权限。

阅读更多: 登录速率限制

13. 以非 root 用户身份运行 Node.js

TL;DR: 通常情况下,Node.js 作为具有无限权限的 root 用户运行。例如,在 Docker 容器中,这是默认行为。建议创建一个非 root 用户,将其编入 Docker 映像(下面给出示例)或代表该用户运行进程,通过使用标志“-u username”来调用容器。

否则: 攻击者成功在服务器上运行脚本后,就可以对本地计算机拥有无限的权限(例如,更改 iptable 并将流量重定向到其服务器)。

阅读更多: 以非 root 用户身份运行 Node.js

14. 使用反向代理或中间件限制负载大小

TL;DR: 负载体积越大,单个线程处理负载的工作量就越大。这是攻击者在不进行大量请求的情况下将服务器拖垮的机会(DOS/DDOS 攻击)。通过在边缘(例如,防火墙、ELB)上限制传入请求的 body 大小或配置 express body parser 仅接受小型负载来减轻此问题。**否则:**你的应用程序将不得不处理大量请求,无法处理其他重要工作,导致性能问题和DOS攻击的漏洞

**阅读更多:**限制负载大小

通过我们的Twitter feed获取每周最佳实践

15. 避免JavaScript eval语句

TL;DR:eval是有害的,因为它允许在运行时执行自定义的JavaScript代码。这不仅是性能问题,也是一个重要的安全问题,因为可能从用户输入源代码恶意的JavaScript代码。另一个应该避免的语言特性是new Function构造函数。也不应该将动态JavaScript代码传递给setTimeoutsetInterval

**否则:**恶意的JavaScript代码会找到一种方法,将一个文本传递到eval或其他实时评估JavaScript语言函数中,并完全访问页面上的JavaScript权限。这种漏洞经常表现为XSS攻击。

阅读更多: 避免JavaScript eval语句

16. 防止恶意RegEx过载单线程执行

**TL;DR:**虽然正则表达式很方便,但对于JavaScript应用程序总体上以及Node.js平台特别存在真正的威胁。文本匹配的用户输入可能需要大量的CPU周期来处理。 RegEx处理可能是低效的,以至于验证10个单词的单个请求可能会阻塞整个事件循环6秒,并将CPU设置为🔥。因此,与其编写自己的正则表达式模式,不如使用第三方验证包,如validator.js,或使用safe-regex检测易受攻击的正则表达式模式。

否则: 编写不良的正则表达式可能容易受到正则表达式DoS攻击的攻击,这将完全阻塞事件循环。例如,流行的moment包在2017年11月发现存在使用恶意RegEx的漏洞。

阅读更多: 防止恶意RegEx

17. 避免使用变量加载模块

TL;DR: 避免使用给定为参数的路径导入另一个文件,因为这可能来自用户输入的原因。这个规则可以扩展到一般的文件访问(即fs.readFile())或其他动态变量来源于用户输入的敏感资源访问。 Eslint-plugin-securitylinter可以捕捉到这样的模式并及早警告。

否则: 恶意用户输入可能会找到用于要求篡改文件的参数,例如文件系统上以前上传的文件,或访问已经存在的系统文件。

阅读更多: 安全的模块加载

18. 在沙箱中运行不安全的代码

TL;DR: 当需要运行在运行时给定的外部代码(例如插件)时,请使用任何一种“沙箱”执行环境,将主代码与插件隔离和保护。这可以通过使用专用进程(例如cluster.fork()),无服务器环境或充当沙箱的专用npm包来实现。

否则: 插件可以通过无限循环、内存超载和访问敏感进程环境变量等各种选项进行攻击。

阅读更多: 在沙箱中运行不安全的代码

19. 在处理子进程时要特别小心

TL;DR: 如果可能,请避免使用子进程,并验证和清理输入,以减轻shell注入攻击。如果必须,优先使用child_process.execFile,它将仅使用一组属性执行单个命令,并且不允许shell参数扩展。

另外: 不加防护的使用子进程可能会导致远程命令执行或shell注入攻击,因为恶意用户输入会传递给未经消毒的系统命令。

了解更多:在使用子进程时要谨慎

20. 对客户端隐藏错误细节

TL;DR: 集成的express错误处理程序默认情况下会隐藏错误详细信息。然而,很有可能你会实现自己的错误处理逻辑,并使用自定义错误对象(许多人认为这是最佳实践)。如果这样做,请确保不要将整个错误对象返回给客户端,因为其中可能包含一些敏感的应用程序详细信息。

否则: 敏感的应用程序详细信息,例如服务器文件路径、正在使用的第三方模块以及应用程序的其他内部工作流程,可能会从堆栈跟踪中找到的信息中泄漏出来,从而被攻击者利用。

阅读更多: 对客户端隐藏错误细节

21. 为npm或Yarn配置双因素认证

TL;DR: 开发链中的任何一步都应该受到MFA(多因素认证)的保护,npm/Yarn是攻击者的甜蜜机会,他们可以获取某些开发人员的密码。使用开发人员凭据,攻击者可以将恶意代码注入到广泛安装于项目和服务中的库中。如果在公共场合发布,甚至可能跨越Web。启用npm的2因素认证几乎为攻击者修改你的包代码留下了零机会。

否则: 你听说过密码被劫持的eslint开发者吗?

22. 修改会话中间件设置

TL;DR: 每个Web框架和技术都有其已知的弱点-告诉攻击者我们使用的Web框架是很有帮助的。使用会话中间件的默认设置可能会使你的应用程序暴露于与“X-Powered-By”标头类似的模块和框架特定的劫持攻击中。尝试隐藏任何标识并揭示你的技术栈(例如Node.js、express)。

否则: 可能会通过不安全的连接发送Cookie,并且攻击者可能会使用会话标识来识别Web应用程序的底层框架,以及模块特定的漏洞。

阅读更多: Cookie和会话安全性

23. 显式设置进程何时崩溃以避免DOS攻击

TL;DR: 当未处理错误时,Node进程将崩溃。许多最佳实践甚至建议即使捕获并处理了错误,也要退出。例如,Express将在任何异步错误上崩溃,除非你使用catch子句包装路由。这为识别使进程崩溃的输入的攻击者打开了一个非常甜蜜的攻击点,并反复发送相同的请求。对此没有即时的解决方案,但可以采用一些技术来缓解痛苦:每当由于未处理的错误而导致进程崩溃时,使用关键性警报,验证输入并避免由于无效的用户输入而使进程崩溃,用catch包装所有路由,并考虑在请求中不崩溃当错误发生在全局范围内时(与全局范围相反)。

否则: 这只是一个有教养的猜测:鉴于许多Node.js应用程序,如果我们尝试将一个空的JSON主体传递给所有POST请求,那么很少有应用程序会崩溃。此时,我们可以重复发送相同的请求轻松地关闭应用程序。

24. 防止不安全的重定向

TL;DR: 不验证用户输入的重定向可能会使攻击者发起钓鱼诈骗、窃取用户凭据和执行其他恶意操作。

否则: 如果攻击者发现你没有验证外部、用户提供的输入,则可能利用此漏洞在论坛、社交媒体和其他公共场所发布特别制作的链接,以便让用户单击它。

阅读更多:防止不安全的重定向

25. 避免将秘密发布到npm注册表

``` TL;DR: 需要注意防止意外将机密信息发布到公共的 npm registry。可以使用 .npmignore 文件来列出需要被屏蔽的特定文件或文件夹,或者在 package.json 文件中使用 files 数组作为白名单。

否则:你的项目的 API 密钥、密码或其他机密信息将被任何发现它们的人滥用,这可能导致财务损失、冒充和其他风险。

阅读更多:避免发布机密信息

感谢作者的努力?请在 GitHub 上给我们的项目点赞

26. 40个通用安全建议清单(与 Node.js 无关)

以下是一些众所周知的、重要的安全措施,应该在每个应用程序中应用。由于它们与 Node.js 框架不一定相关,实现方式也类似 —— 我们在这里将它们作为附录。这些条目按照它们的 OWASP 分类 进行分组。其中一些条目如下:

  • 对 root 账户要求 MFA/2FA
  • 经常更换密码和访问密钥,包括 SSH 密钥
  • 为运维和应用程序用户管理都采用强密码策略,请参见 OWASP 密码建议
  • 不要使用任何默认凭证进行部署或发布,特别是对于管理员用户
  • 仅使用标准的身份验证方法,如 OAuth、OpenID 等 —— 避免使用基本身份验证
  • 身份验证速率限制:在 Y 分钟内禁止进行超过 X 次登录尝试(包括密码恢复等)
  • 在登录失败时,不要让用户知道是用户名还是密码验证失败,只返回一个常见的身份验证错误
  • 考虑使用集中化的用户管理系统,避免管理每个员工的多个帐户(如 GitHub、AWS、Jenkins 等),并从经过实战考验的用户管理系统中受益

完整的 40 个通用安全建议清单可以在官方 Node.js 最佳实践仓库中找到!

阅读更多:40 个通用安全建议

其他值得阅读的文章:

关于作者

译自:https://medium.com/@nodepractices/were-under-attack-23-node-js-security-best-practices-e33c146cb87d

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
一个人玩
先找到想要的,然后出发

评论(0)

添加评论