news 2026/6/9 23:42:06

React 背锅了?一行恶意 JSON 就能让你的 Node.js 服务器瞬间宕机!

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
React 背锅了?一行恶意 JSON 就能让你的 Node.js 服务器瞬间宕机!

近期,Node.js 官方发布了一系列重要的安全更新,修复了 8 个安全漏洞。这次更新涉及 Node.js 20.x、22.x、24.x 和 25.x 等所有活跃版本,影响范围之广,几乎覆盖了所有使用 React Server Components、Next.js 或 APM 监控工具的生产环境应用。

问题到底出在哪里?

在 Node.js 的异步编程世界里,有一个叫async_hooks的底层 API。它的作用是追踪异步操作的生命周期。听起来很技术化,但你可能每天都在用它,只是不知道而已。

React Server Components 用它来追踪渲染上下文,Next.js 用它来追踪请求信息,Datadog、New Relic 等 APM 工具用它来追踪请求链路。可以说,async_hooks已经成为现代 Node.js 应用的基础设施。

但问题就出在这里。当你的代码出现深层递归,导致栈溢出时,正常情况下 Node.js 会抛出一个RangeError: Maximum call stack size exceeded错误,你的try-catch可以捕获它,你的错误处理器可以记录它,然后应用继续运行。

但如果启用了async_hooks,情况就完全不同了。当栈溢出发生时,Node.js 会直接以退出代码 7 终止进程,不经过try-catch,不触发uncaughtException处理器,就这样,应用死了。

为什么这么容易被触发?

需要明确的是,这个 BUG 的触发需要满足几个条件:

  1. 启用了async_hooks(使用 AsyncLocalStorage、APM 工具等)

  2. 代码中存在深度递归

  3. 递归过程中创建了 Promise(触发 async hooks)

  4. 递归深度达到栈溢出的程度

虽然听起来条件挺多,但在实际应用中,这些条件很容易同时满足。让我举个实际的例子。

假设你有一个 Next.js API 路由,用来处理用户上传的 JSON 数据:

export defaultasyncfunction handler(req, res) { try { const data = req.body; const result = processNestedData(data); res.json({ success: true, result }); } catch (err) { // 你以为这里能捕获错误?天真了 console.error('Processing failed:', err); res.status(500).json({ error: 'Processing failed' }); } } function processNestedData(data) { if (Array.isArray(data)) { return data.map(item => processNestedData(item)); } return transform(data); }

一切看起来都很安全,对吧?有try-catch,有错误处理。

但如果有人发送一个嵌套了几千层的 JSON 数组,你的服务器会直接崩溃。不是返回 500 错误,而是整个进程退出,所有正在处理的其他请求都会中断。

这就是一个典型的 DoS(拒绝服务)攻击向量。

技术原因是什么?

问题的根源在于async_hooks的实现方式。当你创建一个 Promise 时,V8 引擎会同步调用 promise hook,这个 hook 会触发 Node.js 的async_hooks回调。

这意味着,每次new Promise()都会在当前调用栈上添加额外的栈帧。当你的代码递归创建 Promise 时,栈上既有用户代码的帧,也有async_hooks的帧。

当栈最终溢出时,抛出错误的那一刻,执行上下文正好在async_hooks的回调里。Node.js 为了避免在 hook 内部出现错误导致的不一致状态,会用一个特殊的错误处理器TryCatchScope::kFatal来包裹这些回调。

kFatal的意思是:如果这里出错了,状态已经不可恢复,直接退出进程。

虽然这个设计的初衷是为了保护应用,但在栈溢出这个场景下,它反而成了问题。因为错误本身来自用户代码,而不是 hook 本身。

Node.js 是怎么修复的?

这次的修复方案很巧妙。Node.js 在TryCatchScope的析构函数里增加了一个检测:如果捕获到的是栈溢出错误,就把它重新抛给用户代码,而不是当作致命错误处理。

TryCatchScope::~TryCatchScope() { if (HasCaught() && mode_ == CatchMode::kFatal) { Local<Value> exception = Exception(); // 检测到栈溢出?重新抛出而不是退出 if (IsStackOverflowError(env_->isolate(), exception)) { ReThrow(); Reset(); return; } // 其他致命错误:按原逻辑退出 FatalException(/* ... */); } }

这样一来,栈溢出错误就能像正常情况下一样被try-catch捕获了。

为什么说这只是缓解措施?

Node.js 官方在博客中特别强调:这只是一个缓解措施(mitigation),而不是根本性的解决方案。

原因很简单:栈溢出的行为本身就不是 ECMAScript 规范的一部分。JavaScript 规范假设栈空间是无限的,没有规定引擎应该在栈溢出时做什么。

抛出可捕获的RangeError只是 V8 等引擎的「尽力而为」行为。依赖这种未定义的行为来保证服务可用性,本身就是有风险的。

正确的做法是:如果你的代码可能处理深度不确定的递归结构(比如用户上传的 JSON),应该主动限制递归深度,或者用迭代算法替代递归。

function processNestedData(data, maxDepth = 100) { function process(item, depth) { if (depth > maxDepth) { thrownewError('Nesting too deep'); } if (Array.isArray(item)) { return item.map(child => process(child, depth + 1)); } return transform(item); } return process(data, 0); }

不要指望运行时帮你兜底。

还有哪些漏洞被修复?

除了这个栈溢出问题(CVE-2025-59466),这次更新还修复了其他几个重要漏洞:

CVE-2025-55131(高危):Buffer 分配时的竞态条件可能导致未初始化的内存泄露,从而暴露敏感信息如 token、密码等。

CVE-2025-55130(高危):通过精心构造的符号链接路径可以绕过文件系统权限模型,读写任意文件。

CVE-2025-59465(高危):发送畸形的 HTTP/2 HEADERS 帧可以让服务器崩溃。

CVE-2026-21636(中危):权限模型可以被 Unix Domain Socket 绕过,访问本地特权服务。

CVE-2026-21637(中危):TLS PSK/ALPN 回调中的异常可能导致进程崩溃或文件描述符泄露。

CVE-2025-59464(中危):处理 TLS 客户端证书时的内存泄漏。

CVE-2025-55132(低危)fs.futimes()可以绕过只读权限修改文件时间戳。

这些漏洞涵盖了内存安全、权限模型、网络协议等多个层面,影响范围确实很广。

哪些版本受影响?

好消息是,Node.js 团队已经为所有活跃版本发布了补丁:

  • Node.js 25.3.0(当前版本)

  • Node.js 24.13.0(LTS)

  • Node.js 22.22.0(LTS)

  • Node.js 20.20.0(LTS)

如果你还在使用更老的版本(18.x 及以下),这些版本已经停止维护,不会收到安全补丁。如果无法升级,可以考虑联系 OpenJS 基金会的商业支持。

特别需要注意的是,如果你在使用 Node.js 24 或更新版本,React 和 Next.js 应用不会受到栈溢出问题的影响,因为AsyncLocalStorage在这些版本中已经用 V8 的新 APIAsyncContextFrame重新实现,不再依赖async_hooks

但是,如果你的 APM 工具直接使用了async_hooks.createHook(),所有版本仍然受影响。


如果你正在生产环境使用 Node.js,特别是:

  • 使用了 React Server Components

  • 使用了 Next.js

  • 使用了任何 APM 工具(Datadog、New Relic、Dynatrace、Elastic APM、OpenTelemetry 等)

  • 使用了AsyncLocalStorage

你应该尽快升级到上述的补丁版本。

同时,检查你的代码中是否有处理不可信输入的递归逻辑,加上深度限制或改用迭代算法。

写在最后

这次漏洞揭示了一个有趣的现象:我们每天依赖的基础设施,可能建立在一些未被明确保证的行为之上。

async_hooks从一个调试 API 发展成了整个生态系统的关键依赖。React、Next.js、所有主流 APM 工具,都在用它。但它的某些边界情况,却从未被充分测试和规范化。

这不是某个框架或工具的问题,而是整个生态系统演化过程中的自然结果。当一个 API 变得足够流行,它的每一个实现细节都可能成为事实标准。

好在 Node.js 团队及时发现并修复了这个问题。但更重要的是,这提醒我们:不要假设运行时会永远按照你期望的方式工作,特别是在处理边界情况时。

防御性编程,永远不过时。

一个有趣的社区讨论

这次漏洞披露后,在开发者社区引发了一些有趣的讨论。

有开发者在推特上评论说:"Next.js 真是被诅咒了,拜托别再试图重新发明 PHP 了,天哪"(next.js is cursed tbh, just stop trying to reinvent php please omg)。

Node.js 核心维护者 Matteo Collina 则直接指出:"核心问题出在 React。"(The "core" problem is in React.)

这个对话很有意思。很多人把矛头指向 Next.js,但技术上说,问题的根源确实在 React Server Components 对AsyncLocalStorage的使用。Next.js 只是在此基础上构建的框架。

这也提醒我们,在讨论技术问题时,准确定位问题的层次很重要。表面上看起来是某个框架的问题,实际上可能是更底层的设计决策带来的影响。

当然,这并不是说 React 或 Next.js 做错了什么。它们使用AsyncLocalStorage是合理的选择,只是碰上了 Node.js 实现中的一个边界情况。技术栈的复杂性就在于此:每一层都在合理地使用下一层的 API,但层与层之间的交互可能产生意想不到的问题。


参考资料:

  • Node.js 官方博客 - Mitigating Denial-of-Service Vulnerability

  • Node.js 2026 年 1 月安全发布公告

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 10:57:58

Llama3-8B支持16k上下文?外推方法实测部署教程

Llama3-8B支持16k上下文&#xff1f;外推方法实测部署教程 你是不是也遇到过这样的问题&#xff1a;想用Llama3-8B处理一份20页的英文技术文档&#xff0c;结果刚输入一半就提示“超出上下文长度”&#xff1f;或者在多轮深度对话中&#xff0c;模型突然忘了前面聊了什么&…

作者头像 李华
网站建设 2026/6/10 10:58:33

微调也能很简单:Qwen2.5-7B + ms-swift极简实践

微调也能很简单&#xff1a;Qwen2.5-7B ms-swift极简实践 你是不是也经历过—— 看到“大模型微调”四个字&#xff0c;第一反应是&#xff1a;装环境、配依赖、改配置、调参数、显存爆炸、训练中断、loss飘忽……最后默默关掉终端&#xff0c;打开ChatGPT继续提问&#xff1…

作者头像 李华
网站建设 2026/6/10 1:13:39

手把手教你用gpt-oss-20b-WEBUI实现AI角色对话

手把手教你用gpt-oss-20b-WEBUI实现AI角色对话 你是否试过和一个真正“有性格”的AI聊天&#xff1f;不是冷冰冰的回答&#xff0c;而是会生气、会害羞、会讲冷笑话、会记住你上次说了什么的数字伙伴&#xff1f;今天不讲理论&#xff0c;不堆参数&#xff0c;我们就用现成的 …

作者头像 李华
网站建设 2026/6/10 12:39:26

AI绘画版权问题:unet生成作品归属权说明

AI绘画版权问题&#xff1a;UNet人像卡通化作品归属权说明 1. 这不是一张普通图片&#xff0c;而是一份需要厘清的权利声明 你刚用“人像卡通化”工具生成了一张可爱又传神的卡通头像——眼睛灵动、线条干净、风格鲜明。它看起来像是随手一拍就能发朋友圈的作品&#xff0c;但…

作者头像 李华
网站建设 2026/5/30 22:09:15

通义千问3-14B部署教程:支持119语互译,低资源语种实测

通义千问3-14B部署教程&#xff1a;支持119语互译&#xff0c;低资源语种实测 1. 为什么是 Qwen3-14B&#xff1f; 如果你正在找一个既能跑在单张消费级显卡上&#xff0c;又能在推理、写作、翻译等任务中接近30B级别模型表现的大模型&#xff0c;那 Qwen3-14B 很可能是你目前…

作者头像 李华
网站建设 2026/6/10 12:46:26

Qwen-Image-2512-ComfyUI与普通ComfyUI有何不同?对比体验

Qwen-Image-2512-ComfyUI与普通ComfyUI有何不同&#xff1f;对比体验 1. 开场&#xff1a;不是“换个模型”那么简单 你有没有试过在ComfyUI里加载一个新模型&#xff0c;点开工作流、输入提示词、点击运行——结果发现界面卡顿、节点报错、出图慢得像在等咖啡凉透&#xff1…

作者头像 李华