なぜ邪悪な正規表現が問題なのですか?
コンピュータは、たとえそれがあなたの意図したものではなく、まったく不合理であっても、あなたが指示したとおりに実行するからです。正規表現エンジンに、特定の入力について、特定のパターンに一致するものと一致しないものがあることを証明するように依頼すると、エンジンは、テストする必要のあるさまざまな組み合わせの数に関係なく、それを実行しようとします。
これは、OPの投稿の最初の例に触発された単純なパターンです。
^((ab)*)+$
入力が与えられた場合:
abababababababababababab
正規表現エンジンは次のようなこと(abababababababababababab)
を試行し、最初の試行で一致が見つかります。
しかし、次にモンキーレンチを投入します。
abababababababababababab a
エンジンは最初に試行(abababababababababababab)
しますが、その余分なために失敗しますa
。これにより、壊滅的なバックトラックが発生します。これは、パターン(ab)*
が誠意を持ってキャプチャの1つを解放し(「バックトラック」)、外側のパターンを再試行できるためです。正規表現エンジンの場合、次のようになります。
(abababababababababababab)
-ノープ
(ababababababababababab)(ab)
-ノープ
(abababababababababab)(abab)
-ノープ
(abababababababababab)(ab)(ab)
-ノープ
(ababababababababab)(ababab)
-ノープ
(ababababababababab)(abab)(ab)
-ノープ
(ababababababababab)(ab)(abab)
-ノープ
(ababababababababab)(ab)(ab)(ab)
-ノープ
(abababababababab)(abababab)
-ノープ
(abababababababab)(ababab)(ab)
-ノープ
(abababababababab)(abab)(abab)
-ノープ
(abababababababab)(abab)(ab)(ab)
-ノープ
(abababababababab)(ab)(ababab)
-ノープ-
(abababababababab)(ab)(abab)(ab)
ノープ-
(abababababababab)(ab)(ab)(abab)
ノープ-
(abababababababab)(ab)(ab)(ab)(ab)
ノープ-
(ababababababab)(ababababab)
ノープ-
(ababababababab)(abababab)(ab)
ノープ
(ababababababab)(ababab)(abab)
-ノープ
(ababababababab)(ababab)(ab)(ab)
-ノープ
(ababababababab)(abab)(abab)(ab)
-ノープ
(ababababababab)(abab)(ab)(abab)
-ノープ
(ababababababab)(abab)(ab)(ab)(ab)
-ノープ
(ababababababab)(ab)(abababab)
-ノープ
(ababababababab)(ab)(ababab)(ab)
-ノープ
(ababababababab)(ab)(abab)(abab)
-いいえ
(ababababababab)(ab)(abab)(ab)(ab)
-いいえ
(ababababababab)(ab)(ab)(ababab)
-いいえ-いいえ-いいえ-いいえ-いいえ...-
(ababababababab)(ab)(ab)(abab)(ab)
いいえ-いいえ-いいえ-いいえ-いいえ-いいえ-いいえ-いいえ-いいえ
(ababababababab)(ab)(ab)(ab)(abab)
(ababababababab)(ab)(ab)(ab)(ab)(ab)
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abababab)
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ababab)(ab)
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abab)(abab)
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abab)(ab)(ab)
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ababab)
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abab)(ab)
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abab)
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)
可能な組み合わせの数は、入力の長さに応じて指数関数的に増加します。正規表現エンジンは、考えられるすべての用語の組み合わせを使い果たして、最終的にあきらめるまで、この問題を解決しようとしてすべてのシステムリソースを使い果たします。 「一致するものはありません」と報告します。その間、サーバーは溶けた金属の燃える山に変わりました。
邪悪な正規表現を見つける方法
それは実際には非常にトリッキーです。現代の正規表現エンジンでの壊滅的なバックトラックは、アランチューリングが解決することが不可能であると証明した停止問題と本質的に似ています。問題のある正規表現を自分で作成しましたが、それらが何であるか、一般的にはそれらを回避する方法を知っています。アトミックグループで可能なすべてをラップすると、バックトラックの問題を防ぐのに役立ちます。これは基本的に、正規表現エンジンに、指定された式を再訪しないように指示します。「最初の試行で一致したものはすべてロックします」。ただし、アトミック式は式内のバックトラックを妨げないため、危険^(?>((ab)*)+)$
です^(?>(ab)*)+$
が安全です(一致します)。(abababababababababababab)
次に、一致する文字を放棄することを拒否し、壊滅的なバックトラックを防ぎます)。
残念ながら、一度作成すると、問題の正規表現をすぐにまたはすばやく見つけることは実際には非常に困難です。結局のところ、悪い正規表現を認識することは、他の悪いコードを認識することと同じです。多くの時間と経験、および/または単一の壊滅的なイベントが必要です。
興味深いことに、この回答が最初に書かれて以来、テキサス大学オースティン校のチームは、これらの「邪悪な」パターンを見つけるという明確な目的で正規表現の静的分析を実行できるツールの開発について説明した論文を発表しました。このツールはJavaプログラムを分析するために開発されましたが、特にReDoS攻撃の割合が増え続けるにつれて、JavaScriptやその他の言語で問題のあるパターンを分析および検出することを中心に開発されたツールが今後さらに増えると思います。
正規表現を使用するプログラムでのDoS脆弱性の静的検出
ValentinWüstholz、Oswaldo Olivo、Marijn JH Heule、およびIsilDillig
テキサス大学オースティン校