FlawOpen

flawopen.com/ReDoS/JavaScript RegExp

JavaScript RegExp 会受到 ReDoS 影响吗?

CWE-1333: Inefficient Regular Expression Complexity参考页面
简短回答

想象一名工作人员在缺少最后印章时,反复尝试拆分一叠表单的各种方式:含糊的模式会让正则引擎重复检查相同字符。V8 常用的 RegExp 路径可能这样回溯;限制输入并使用工作量可预测的模式可以控制开销。在 Node 中,同步匹配运行于事件循环线程,耗时过长会延迟该进程中的其他回调。 V8 文档介绍了一个通过标志启用且有模式限制的实验性非回溯引擎;不要假定生产环境中的 Node 已启用它。

有风险
// 请求数据上的含糊嵌套重复
const re = /^(\w+\s?)*$/;

app.post('/search', (req, res) => {
  const q = req.body.q;
  if (!re.test(q)) return res.sendStatus(400);
  res.sendStatus(200);
});
已修复
// 限制输入,只接受字段规定的语法
app.post('/search', (req, res) => {
  const q = req.body.q;
  if (typeof q !== 'string' || q.length > 128)
    return res.sendStatus(400);

  // 示例语法:仅 ASCII 单词字符和空格
  if (!/^[\w ]{1,128}$/.test(q))
    return res.sendStatus(400);

  res.sendStatus(200);
});

为什么 Node.js 会放大 ReDoS 影响

同步正则匹配运行在处理 Node.js 进程回调的 JavaScript 线程上。如果模式在近似匹配或失败输入上耗时很长,匹配返回前事件循环无法运行其他回调或推进请求。Worker、多进程或副本可能保留部分容量,但受影响的进程仍会阻塞。

实际影响取决于可达性、模式复杂度、输入限制和部署拓扑。攻击者必须让输入到达易受影响的匹配,并触发其高开销路径。应将其视为受影响进程的可用性风险;整个服务是否不可用取决于 worker、副本、路由和健康检查。

npm 真实案例:xmldom 的 CVE-2026-83606

针对 @xmldom/xmldom 的审查公告报告了处理处理指令时的 CWE-1333:易受影响的语法允许贪婪空白组和惰性数据组消费相同空白。缺少结尾 ?> 时,解析器会尝试许多拆分,耗时呈二次增长。受影响版本为 0.9.0-beta.9 至 0.9.10;0.9.11 阻止分隔符把空白重新交给数据组,从而修复问题。公告将范围限定在带 scope 的 0.9.x 路径,没有将 0.8.x 或无 scope 包列为受影响版本。

依赖扫描有用,但不能证明正则安全

npm audit 报告项目依赖树中已知公告匹配项;它不会分析源代码中的每个正则表达式,也不会发现尚未发布的 ReDoS 缺陷。更新直接和传递依赖,检查公告涉及的调用路径,并单独审查处理用户控制文本的模式。

npm audit --omit=dev

审计只覆盖已知公告,并非所有正则风险。结合部署情况审阅报告,并另行检查不可信输入能否触发高开销匹配以及输入长度是否受限。

常见问题

可以为 JavaScript 正则设置超时吗?

RegExp API 没有单次匹配超时选项。优先使用简单模式,并在匹配前限制输入。若必须执行复杂匹配,可放入带强制终止期限的 worker 或子进程隔离;这能限制影响,但不会让模式本身变高效。

浏览器中的 JavaScript 也会受影响吗?

会。异常缓慢的同步匹配可能在返回前冻结页面主线程并阻塞界面。限制输入、简化模式,避免每次按键都执行昂贵匹配。

搜索时使用 new RegExp(userInput) 安全吗?

将用户提供的模式视为可能导致高开销的可执行输入。服务器不要在没有严格限制和隔离的情况下接受任意模式。如果只需字面搜索,应将输入转义为正则字面量或使用字符串搜索 API;转义不能取代长度限制。

参考资料与修复

另请参阅 什么是 ReDoS(灾难性回溯)?