ルール
低速な 正規表現に 対する 防御。
ネストされた 量指定子や
曖昧な パターンを 持つ 正規表現は、 壊滅的な
バックトラッキングや パフォーマンスの 問題を引き起こす 可能性があります。
サポート言語: 45+はじめに
正規表現は、適切な入力によってアプリケーションを数秒から数分間フリーズさせる可能性があります。壊滅的なバックトラッキングは、正規表現エンジンがパターンを一致させようとする際に、指数関数的に増加するパスを探索するときに発生します。次のような正規表現は、 (a+)+b 有効な入力に一致させるにはマイクロ秒かかりますが、末尾にbがないaの文字列を拒否するには何時間もかかることがあります。攻撃者は、正規表現サービス拒否(ReDoS)攻撃を通じてこれをエクスプロイトし、リクエストタイムアウトが発生するかプロセスがクラッシュするまで、正規表現エンジンがCPUを100%消費するような細工された入力を送信します。
なぜ重要なのか
セキュリティ上の影響(ReDoS攻撃): 攻撃者は、巧妙に作成された入力を含む単一のリクエストでアプリケーションを麻痺させることができます。メール検証やURL解析パターンが一般的な標的です。帯域幅を必要とする従来のDoS攻撃とは異なり、ReDoSはごくわずかなペイロードしか必要としません。
パフォーマンスの低下: 通常のユーザー入力が壊滅的なバックトラッキングを引き起こし、応答時間がミリ秒から秒単位に急増する可能性があります。これは特定の入力パターンでのみ現れるため、デバッグが困難な予測不能なレイテンシーを引き起こします。
本番環境でのインシデント: 脆弱な正規表現は、Node.jsのイベントループをブロックしたり、スレッドプールリソースを消費したりします。リクエストが蓄積されるにつれてメモリが増加し、システムが応答しなくなります。マイクロサービスでは、1つの脆弱な正規表現が依存サービスに障害を連鎖させます。
検出の困難さ: 短い入力でテスト中に問題なく機能するパターンは、長い入力では指数関数的に遅くなります。脆弱性はしばしば本番環境まで見過ごされ、アクティブなインシデント中に緊急デプロイが必要となります。
コード例
❌ 非準拠:
function validateEmail(email) {
const regex = /^([a-zA-Z0-9_\-\.]+)+@([a-zA-Z0-9_\-\.]+)+\.([a-zA-Z]{2,5})$/;
return regex.test(email);
}
function extractURLs(text) {
const regex = /(https?:\/\/)?([\w\-])+\.(\w+)+([\w\-\.,@?^=%&:/~\+#]*)+/g;
return text.match(regex);
}
なぜそれが安全でないのか: ネストされた量指定子 ([a-zA-Z0-9_\\-\\.]+)+ 指数バックトラッキングを作成します。例えば、次のようなメールの場合: aaaaaaaaaaaaaaaaaaaaaaaaa!、正規表現エンジンは失敗するまでに無数の組み合わせを試行します。URL正規表現には複数のネストされた量指定子があり、問題が複雑化するため、期待される構造を持たない有効な文字の長い文字列のような入力で簡単にエクスプロイト可能になります。
✅ 準拠済み:
function validateEmail(email) {
const regex = /^[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9_\-\.]+\.[a-zA-Z]{2,5}$/;
return regex.test(email);
}
function extractURLs(text) {
const regex = /https?:\/\/[\w\-]+\.[\w\-]+(?:[\w\-\.,@?^=%&:/~\+#]*)?/g;
return text.match(regex);
}
安全な理由: ネストされた量指定子を削除することで、壊滅的なバックトラックがなくなります。[a-zA-Z0-9_\-\.]+のような単一の量指定子は線形時間で実行されます。URLパターンは、ネストされた繰り返しではなく、オプションのサフィックス (?:...)? を持つ非キャプチャグループを使用しており、入力の長さや内容に関わらず予測可能なパフォーマンスを保証します。
まとめ
正規表現のパフォーマンスは、最適化だけでなくセキュリティ上の懸念事項です。ネストされた量指定子、繰り返しグループ内の重複する文字クラス、および曖昧な代替パターンについて、すべての正規表現パターンを確認してください。デプロイ前に壊滅的なバックトラッキングを特定するために、病的な入力(有効な文字の長い文字列の後に無効な末尾が続くもの)で正規表現パターンをテストしてください。可能な場合は、複雑な正規表現を、予測可能なパフォーマンス特性を持つ文字列解析関数に置き換えてください。

