flawopen.com/ReDoS/JavaScript RegExp
JavaScript RegExp 会受到 ReDoS 影响吗?
想象一名工作人员在缺少最后印章时,反复尝试拆分一叠表单的各种方式:含糊的模式会让正则引擎重复检查相同字符。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
审计只覆盖已知公告,并非所有正则风险。结合部署情况审阅报告,并另行检查不可信输入能否触发高开销匹配以及输入长度是否受限。
常见问题
RegExp API 没有单次匹配超时选项。优先使用简单模式,并在匹配前限制输入。若必须执行复杂匹配,可放入带强制终止期限的 worker 或子进程隔离;这能限制影响,但不会让模式本身变高效。
会。异常缓慢的同步匹配可能在返回前冻结页面主线程并阻塞界面。限制输入、简化模式,避免每次按键都执行昂贵匹配。
将用户提供的模式视为可能导致高开销的可执行输入。服务器不要在没有严格限制和隔离的情况下接受任意模式。如果只需字面搜索,应将输入转义为正则字面量或使用字符串搜索 API;转义不能取代长度限制。
参考资料与修复
- GitHub 公告 — @xmldom/xmldom 的 CVE-2026-83606
- xmldom — 0.9.11 版本说明
- Node.js — 不要阻塞事件循环(ReDoS)
- V8 — 非回溯 RegExp 引擎及限制
- npm — 依赖审计
- CWE-1333 — 正则复杂度低效