我们平时开发时,会经常使用到正则表达式进行对数据检验,因为这样很方便快捷。但是与此同时,却悄悄地隐含了一些安全问题。下面我们就说一说ReDos,即正则表达式拒绝服务。此安全问题,常常是出现在web api请求检验时,开发者为贪图方便快捷而使用正则表达式对用户请求的数据进行简单校验。
以下以Nodejs为案例,如下面代码:
let regx = /^([a-z0-9A-Z]+[-|_|\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\.)+[a-zA-Z]{2,}$/; //校验Email的正则
let str = "nnnnnnnnnardy"; //长度为13的字符串
console.time("redos");
regx.test(str);
console.timeEnd("redos");
我的电脑是windows 10
,台式机,8G 内存,4核心,测得的平均耗时为0.245ms
;
接下来,我们把要检验的字符串长度加大:
let str = "nnnnnnnnnnnnnnnnardy"; //长度为20的字符串
此时测试的平均耗时为5ms
;我们继续把字符串长度增大:
let str = "nnnnnnnnnnnnnnnnnnnnnnnnnnardy"; //长度为30的字符串
此时测试的平均耗时为5s
;我们继续把字符串长度增大:
let str = "nnnnnnnnnnnnnnnnnnnnnnnnnnnardy"; //长度为31的字符串
Oh My God!此时测试的最高耗时居然达到了10s
;我还试了35位长度的字符串,反正我等了2分钟没等到结果返回。
试想,如果我们的web接口,也加入了类似的正则校验,那仅仅是因为用户的Emai的名称长了点,就足以影响到服务器的请求接收,这对项目来说是一个安全问题了。
为什么会出现ReDos??
原因是:大多数正则表达式引擎的工作原理相似,都会首先对合适的字符串进行尝试多匹配,一次失败了,会尝试下一个可能的匹配,如果所有可尝试匹配情况都失败后,就会进行回溯,看是否有其他方法来消化前一个字符,如果匹配路径太深且发现最后不匹配,又或者有多种匹配路径,则最后回溯的步骤可能会变得非常大,从而导致了灾难性的回溯结果。
建议
- 不要对用户输入的或不确定长度的数据进行正则检验
- 尽可能找到适合的替代方法
附注
因为各种开发语言都有正则表达式,且实现方式相似,所以,ReDos并非局限于Nodejs一种语言。