#3119·joi

通过 string.pattern() 进行 ReDoS,使用灾难性回溯正则表达式阻塞事件循环

作者: zhangjiashuo-cs创建于 2026年5月24日更新于 2026年7月12日

说明

Joi.string (.pattern (re)'直接使用regExp.test ()',没有安全包装。 当 schema 开发者通过(或在运行时接受)有嵌入式修饰(如"((a+)+)"+$")的regex时,验证甚至短攻击者控制的字符串会将事件回路阻断一秒到一分——经典的常规-Expression Deligial-Off-Service(ReDoS).

复制(joi 17.13.3)

zz
Const Joi = 需要 ('joi' );

const schema = joi.string (. pattern (/(a+)+)+ $/;
const 输入 = 'a'.repeat (22) +'!'; // 23个字符

const t0 = 日期. now();
schema.validate(输入);
a. 控制台. log( “已消除” 、 Date. now( ) - t0, “ms” );
//已过期 / 5000ms( 屏蔽事件循环)

每增加一个`a'大约是时间的两倍:

  • 输入长度 * * * * 已过* * |-|-|-.
  • 18 + ! + ~ 300 ms → + 18 + ! ~ 20 + !" ~ 1.2 s ~ |.
  • 22 + ! + ~ 5 s + + | ~ 24 + ! ~ 20 s ~ ~ ~ ~ 24 + ! ~ ~ ~ 20 s ~ ~
  • 28 + ! + 5 分钟 + 5 分钟

(a*)*美元'、(a*aa)美元'、`(aa?)*美元'等图案表现了同样的行为。

失败的属性

[JavaScript] 从“快速检查”中导入 fc; 从“joi”中导入Joi;

// 一个 schema 开发者从经过验证的部件中构成一个图案; 是否 / 由此产生的 regex 是 ReDoS- safe 在编译时并不明显. fc.sert(fc.property) (英语). fc.整数({min: 1, 最大: 30}), (n) {{{ const schema = joi.string (. pattern (/(a+)+)+ $/; const t0 = 日期. now(); schema.validate ("a". repeat(n) + "!"); s. 返回( Date.now () - t0) < 1000; // < 1 s 用于任何小输入 {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}你觉得呢? 页:1 // Shrinks 为 n=22


威胁模式

两种现实的攻击情景:

1. ** 公共监管-保密终点**: 一个API允许用户指定一个"名称模式","URL模式",或类似自定义验证 regex(joi-based JSON Schema admin工具,低码格式构建器). 一个恶意用户提供灾难性回溯模式;随后通过它的请求将服务器挂起.
2. **利用用户提供的regex** 构建的系统计划,利用用户输入(如:`Joi.string (.pattern (new RegExp (req.body.pattern))'):直接处理通过该处理器的每一项请求。

即使对于硬编码regex的策略,第三方的策略库,将用户输入到joi模式中,也会继承这个问题.

建议改正

将“regex.test(value)”呼叫中包含:

1. ** 同步超时** 通过`vm.run InNewContext({timeout:N})' (rough,但只为 N ms 屏蔽事件循环)。
2. ** 与Thompson-NFA后置发动机相匹配**的Joi模式(例如`re2'通过`re2'/`re2-WASM')。 默认为安全引擎;让用户选择进入本地的“RegExp”并有明确的警告。
3. **至少**:利用静态分析(如`安全记录 ' 或`再检查 ' ),在登记时间 " pattern(re) " 检测灾难性回溯模式,并拒绝或警告。

" Yup " 、 " Zod " 和 " jav " 都在记录ReDoS的关切方面趋于一致;其中许多人提出了`format: re2 ' 选项。 Joi没有文件

环境

- 乔伊:17.13.3
- 节点:20+