luokuning / blogs

翻译,随笔,以及懒得整理……
81 stars 2 forks source link

关于 Node.js 正则表达式拒绝服务攻击 #13

Open luokuning opened 5 years ago

luokuning commented 5 years ago

从 npm audit 说起

今天尝试用 npm audit 在项目里跑了一下,发现了很多类似下面的错误:

image

如果你还不清楚 npm audit 是做什么的,可以参考官方文档。简单来说 npm audit 会对项目的依赖包进行安全扫描,并且列出存在的可能遭受攻击的安全隐患,比如上面截图中的信息就是一个潜在的被攻击点。

Regular Expression Denial of Service (ReDoS)

图片第一行显示的 Regular Expression Denial of Service 成功引起了我的注意。Denial of Service 我们都知道,拒绝服务攻击,以一种简单暴力的方式消耗被攻击者的资源,让其服务到达不可用的地步,比如常见的 DDoS。那么 Regular Expression Denial of Service (以下简称 ReDoS) 通过字面意思理解应该就是:正则表达式拒绝服务攻击。

正则表达式的匹配通常是比较耗费计算性能的,尤其是当正则表达式中包含复杂的子表达式的重复匹配、或者重复匹配的简单子表达式也可能匹配另一个子表达式的时候。当一个正则表达式存在这些问题的时候,一些用户的输入可能会导致匹配运行效率非常低下,从而引发 DoS (因为计算资源可能被耗尽)。

Node.js 中的 ReDoS

说了这么多,也许不如一个例子能够把我的意思表达的更清楚:

image

测试环境:MacBook Pro (Retina, 15-inch, Mid 2015), 2.2 GHz Intel Core i7

上面正则表达式看起来非常简单,但是存在我们上面说的可能导致匹配效率低下的问题。可以看到这个简单的匹配耗时大约 5 秒钟,对于 Node.js 这种基于 event loop 的单进程服务器来说是不可接受的。

解决办法

尽量使用社区开源的正则表达式。如果你正在做一些常规的字符串校验,比如邮件、电话号码的匹配,那么可以尝试 validator.js

如果需要自己编写正则表达式,可以使用 safe-regex 这样的工具来校验自己的正则表达式是否有潜在的问题,也可以使用 regex101 在线进行检验 (regex101 功能十分强大,界面也很清新,另外还提供了 debugger 功能),比如上面的这个例子在 regex101 里会被检测到有 “灾难性的回溯”(Catastrophic backtracking) 问题:

image

附录

出于好奇,用 Rust 写了跟上面 Node.js 例子中的正则表达式,下面是执行的结果:

image

Rust 好像对于正则表达式这些潜在的性能问题有一些预防的解决方案 (只调用回溯引擎的混合解决方案),具体我也不是太了解,有兴趣的小伙伴可以自行查资料。不过 Rust 确实是非常快的,真的要抽空好好学习 Rust 咯,后面用得到的地方还是很多的。