介绍
当您想到拒绝服务攻击时,您会想到什么?可能是一大群机器人试图访问 Web 服务器的资源以使其瘫痪。好吧,这肯定是导致拒绝服务攻击的一种方式。但是,还有一种您可能没有听说过的方式。它被称为 ReDoS,是由正则表达式引起的。
正则表达式?但这怎么可能呢?那不是通过使用过滤器来匹配字符串、将字符串列入白名单和黑名单,从而使我们的工作更轻松吗?是的,但是攻击者也可以利用它来使应用程序(服务器)屈服。让我们了解如何!
正则表达式是什么?
简而言之,正则表达式是一种用于匹配(编程语言中)字符串的模式。让我们通过一个示例来理解它吧,该示例是“用正则表达式在服务器端验证电子邮件地址”。
代码语言:javascript复制let regex = new RegExp('[a-z0-9] @[a-z] .[a-z]{2,3}');
let testEmails = ["notanemail.com", "workingexample@email.com", "another_working@somethingelse.org", "notworking@1.com"];
testEmails.forEach((address) => {
console.log(regex.test(address))
});
上面是一段 JavaScript 代码(译者注:不会 JS 也无妨,对阅读本文的影响不大,请继续阅读)。我们在这里使用的正则表达式是 [a-z0–9] @[a-z] .[a-z]{2,3}
。我们提供了几个电子邮件地址,然后我们需要检查它们是否遵循电子邮件地址的一般模式。让我们分解一下正则表达式。
[a-z0–9]
:表示此处的字符串可以是任何小写字母和数字。末尾的加号 ( ) 表示必须至少有一个字符(无论是小写字母还是数字)。@
:表示此处应该有 AT(@)符。[a-z]
:表示此处字符串应该包含(一个或多个)小写字母的字符.
:表示此处应该有一个点(.)[a-z]{2,3}
:表示此处字符串是由小写字母组成的,但其长度只能是 2 或 3。
让我们将其与我们选择的电子邮件 ID 进行比较。让电子邮件 ID 为 yourremail12@yahooemail.com
。
youremail12@
对应于[a-z0-9] @
yahooemail
对应于[a-z]
.com
对应于.[a-z]{2,3}
这通常是正则表达式的工作方式。但这不是会按预期工作吗?这如何能导致拒绝服务攻击呢?让我们了解一下。
正则表达式由正则表达式运算器处理。在 ReDoS 攻击期间,攻击者通过提供输入字符串强制正则表达式运算器陷入循环。当它处于循环中时,正则表达式运算器可能会花费大量时间,并消耗大量资源。这会导致其他合法客户端无法使用资源,并可能导致 Web 服务器和应用程序无响应并最终崩溃。
另一种情况可能是设计不良的正则表达式模式,这可能导致输入验证失败,在正则表达式运算器解析时会消耗大量时间等。
有害正则表达式(Evil Regex)
有害正则表达式模式是攻击者可以利用的正则表达式。根据 Wikipedia,这些是有害正则表达式模式的特征。
- 正则表达式将重复(
*
)应用于复杂的子表达式。 - 对于重复的子表达式,存在一个匹配,同时该匹配也是另一个有效匹配的后缀。
这说明了如果已经有一个复杂的子表达式,并且应用一些像
或 *
这样的贪婪运算符,可能会导致一个有害的正则表达式。
例如,
我们考虑以下正则表达式:^(a ) $
^
和 $
定义了字符串必须分别以 a
开头和结尾。
如果我们提供 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
,正则表达式运算器将在几毫秒内处理它并返回 True
。
如果我提供 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!
,你觉得会发生什么!
如果您运行它,则对于所提供的正则表达式,(在返回 False
之前)需要花费大约 2 秒的时间来处理。只是通过添加一个额外的感叹号会导致这么长的时间吗?
让我们从正则表达式运算器的角度来看看它是如何工作的。正则表达式运算器直到达到感叹号之前将会正常工作。由于我们没有指定查找 (!
) 标记,因此它会回溯到前面的字母,并查看是否有另一种方法来解析先前的字符,以便可以验证整个字符串。在它最终发现绝不会返回 True
之前,这样的回溯将会一直持续下去。
如您所知,回溯会导致大量时间消耗。攻击者可以使用它来利用有害正则表达式模式。
译者注
看完上述内容后,有些读者可能仍对刚才的示例(回溯)表示不解。为了让读者能够更加清晰地了解其原理,我将上例中正则表达式运算器可能将执行的步骤进行了分解演示。
下列演示的过程只是存在的一种可能(实际情况取决于正则表达式引擎的实现)
回溯次数 | 解析结果 |
---|---|
0 | {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa} |
1 | {aaaaaaaaaaaaaaaaaaaaaaaaaaaaa}, {a} |
2 | {aaaaaaaaaaaaaaaaaaaaaaaaaaaa}, {aa} |
3 | {aaaaaaaaaaaaaaaaaaaaaaaaaaaa}, {a}, {a} |
4 | {aaaaaaaaaaaaaaaaaaaaaaaaaaa}, {aaa} |
5 | {aaaaaaaaaaaaaaaaaaaaaaaaaaa}, {aa}, {a} |
6 | {aaaaaaaaaaaaaaaaaaaaaaaaaaa}, {a}, {aa} |
7 | {aaaaaaaaaaaaaaaaaaaaaaaaaaa}, {a}, {a}, {a} |
8 | {aaaaaaaaaaaaaaaaaaaaaaaaaa}, {aaaa} |
... | ... |
2147483647 | {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a}, {a} |
从上面的表格可以看出,正则引擎可能会做出 (近 20 亿)种解析结果。
也就是,对于示例给出的有害正则表达式和恶意字符串。如果按照我们的演示进行,设有效最长子串的长度为 ,那么回溯的时间复杂度为 (证明过程略,感兴趣的读者可去探究帕斯卡三角形)。该时间复杂度是指数级别,多么可怕的时间复杂度级别啊!
对于 30 个 a,仅多出一个感叹号,执行时间竟然多出约 20 亿倍。当然了,这里仅是一种可能的演示,在作者示例中的时间仅多了千倍左右。这可以从很多方面解释,例如正则引擎的某些优化,或比例受某些较长固定执行时间的支配等。
译者注结束
可利用性
开发人员和我们一样都是人类,因此都会出错。他们创建的许多应用程序可能会包含可利用的正则表达式模式。对于有经验的黑客或安全专家来说,正则表达式模式很容易被猜到,而在有些情况下,源代码是可以在线获得的。攻击者可以利用这一点来谋取利益,并可能降低被攻击者的利益。
糟糕的正则表达式模式也可能会导致绕过许多安全控制,无论是在 Web 应用程序还是网络防火墙上。
因此,始终建议在将应用程序部署到生产环境之前正确测试正则表达式模式。
缓解措施
- 缓解这种情况的最佳方法是根本不去使用它。尤其当你是新手时。
- 如有必要,请使用安全的正则表达式。这样只需寻找另一种替换方法来完成相同的输出。
- 在使用正则表达式之前,请在 regex101.com 或其他在线资源等网站上测试它们。
- 安全扫描用户输入永远不会过时。
- 现在存在着很多正则表达式运算器。我建议您使用更高效的正则表达式引擎。你可以看看 https://github.com/google/re2。
- 测试您是否正在使用易受攻击的正则表达式引擎或运算器。例如,url-regex 是一个用于测试匹配 URL 的正则表达式的包。
结论
我们忽视的诸如此类的小事可能会给安全增加了很大的风险。在这篇文章中,我们了解了可能由有害正则表达式模式引起的 DoS 攻击、主要原因、以及我们应如何缓解它们。有必要缓解这种情况,因为它们会影响网站的可用性,并且可能会花费您很多资金。
链接:https://medium.com/codex/redos-regular-expression-denial-of-service-fefdce7ee076
(版权归原作者所有,侵删)