MENU

ReDoS(正規表現DoS)とは?仕組み・被害例・対策をわかりやすく解説

正規表現(regex)は、メールアドレスのバリデーションやURLのパース、パスワード強度チェックなど、Webアプリケーション開発に欠かせないツールです。しかし、特定の書き方をした正規表現は、攻撃者が細工した文字列を1つ送るだけでCPUを使い果たし、サーバーが応答不能に陥ります。これが ReDoS(Regular Expression Denial of Service) と呼ばれる攻撃手法です。

「うちもバリデーションで正規表現を使っているけど、それが攻撃の入口になるとは知らなかった」──そう感じているエンジニアは少なくありません。本記事では、ReDoSの仕組みから実際の被害例、今日から導入できる対策まで、現場目線で解説します。

ReDoS(正規表現DoS)とは?仕組み・被害例・対策をわかりやすく解説 - 解説

目次

ReDoSとは?なぜ危険なのか

ReDoS は、正規表現エンジンの処理時間を爆発的に増大させることでサービス妨害(DoS)を引き起こす攻撃手法です。通常の文字列では瞬時に終わる正規表現のマッチング処理が、細工された入力に対して指数関数的な時間を要し、サーバーのCPUが占有されます。

一般的なDDoS攻撃は大量のリクエストを送りつけてサービスを妨害しますが、ReDoSはたった1リクエストでサーバーを数十秒~数分間応答不能にできる点が厄介です。レートリミットやWAFを導入していても、正規表現そのものに問題があれば防ぎようがありません。

影響を受ける言語・ランタイムは幅広く、JavaScript(Node.js)・Python・Java・PHP・Rubyなど、バックトラッキング型の正規表現エンジンを採用しているほぼすべての環境で発生します。

・攻撃コストが低い: 特殊なツールは不要。細工した文字列を1つ送るだけ
・防御が難しい: ファイアウォールやWAFだけでは防げない
・気づきにくい: ログに残る前にサーバーがハングするケースがある
・外部依存も危険: 自分のコードが安全でも、利用ライブラリが脆弱なことがある

攻撃の仕組み──バックトラッキングが起こす指数爆発

1. 正規表現エンジンの動作原理

多くの正規表現エンジンは「NFA(非決定性有限オートマトン)」方式で動作します。パターンに対してマッチする経路を複数試し、ある経路が失敗したら別の経路に戻る(バックトラック)という仕組みです。通常の正規表現ではこの回数は少なく、数マイクロ秒で完了します。

2. 壊滅的バックトラッキング(Catastrophic Backtracking)

ところが、「曖昧さ」を持つパターンでは、入力文字列の長さに対してバックトラック回数が指数関数的に増えます。以下は脆弱なパターンの代表例です。

# 量化子のネスト(最も危険) (a+)+ # グループの繰り返しと選択の組み合わせ ([a-zA-Z]+)* # 同じ文字列にマッチする重複した選択肢 (a|aa)+

たとえば `(a+)+` というパターンに `”aaaaaab”` という文字列(末尾に `b`)を与えると、エンジンは「`a` をどのグループにどう分割するか」の全組み合わせを試みます。文字列の長さが n のとき、試行回数が 2n のオーダーになります。n=30程度でサーバーが数十秒間応答不能になることも珍しくありません。

3. 実際の被害事例

これは理論上の話にとどまりません。過去には多くのライブラリや本番サービスで被害が発生しています。

・npmパッケージ「moment」: 日付文字列のパース処理でReDoSが発生し、CVE-2022-24785として修正されました
・ua-parser-js: User-Agent文字列の解析に使われる人気ライブラリで複数のReDoSが発見されました
・メールバリデーションの事例: 複雑なメールアドレス検証正規表現が原因で、サービスのAPIエンドポイントが数分間ダウンした事例が複数報告されています
・Stack Overflow(2016年): Markdownパーサーの正規表現にReDoS脆弱性があり、34文字の入力でCPU使用率100%・サービス停止が発生しました

具体的な防御手順

1. 脆弱なパターンを書かない

最も効果的な対策は、脆弱なパターンを最初から書かないことです。以下の3点を押さえるだけで大半のリスクを回避できます。

・量化子のネストを避ける: `(a+)+` `(.*)+` のような書き方をしない
・選択肢の重複を排除する: `(a|aa)+` のように同じ文字列にマッチしうる選択肢を並べない
・アンカーを活用する: `^` と `$` を使って一致範囲を限定することで、バックトラックを大幅に減らせる

メールアドレスの検証には、RFC準拠の複雑な正規表現より、シンプルな `[^@\s]+@[^@\s]+\.[^@\s]+` 程度で実運用上は十分なことが多く、この形なら問題は発生しません。

2. 静的解析ツールでコードをスキャンする

既存コードに脆弱なパターンが混入していないかを確認するツールが公開されています。

# Node.js環境での静的チェック(safe-regex) npm install -g safe-regex safe-regex '(a+)+' # => false(脆弱と判定) safe-regex '[a-z]+' # => true(安全と判定) # Pythonプロジェクトのスキャン(vuln-regex-detector等) # CI/CDパイプラインに組み込むことで新規コードへの混入を防ぐ

3. 入力値の長さを制限する

バックトラック回数は入力文字列の長さに依存します。ユーザー入力を正規表現で処理する前に最大文字数を制限することで、指数爆発を防げます。

# メールアドレスはRFC 5321準拠で最大254文字 if len(email_input) > 254: raise ValueError("入力が長すぎます") # パスワードは100文字以内に制限(bcryptの72文字上限とも整合) if len(password_input) > 100: raise ValueError("入力が長すぎます")

4. タイムアウトを設定する

万が一脆弱なパターンが混入していた場合でも、正規表現の処理にタイムアウトを設けることで被害を最小化できます。

# Python + SIGALRM によるタイムアウト(Unix系環境) import re import signal def timeout_handler(signum, frame): raise TimeoutError("正規表現処理タイムアウト") signal.signal(signal.SIGALRM, timeout_handler) signal.alarm(2) # 2秒でタイムアウト try: result = re.match(pattern, user_input) except TimeoutError: result = None # タイムアウト時はマッチ失敗として処理 finally: signal.alarm(0)

5. 線形時間保証エンジンへの移行を検討する

バックトラッキングを行わないDFAベースの正規表現エンジンを採用することで、ReDoSを根本的に排除できます。

・Go言語の `regexp` パッケージ: DFAベースで実行時間が入力長に対して線形保証。性能劣化が起きない
・Rustの `regex` クレート: 同様に線形時間保証。メモリ安全性も高い
・RE2ライブラリ(C++/Go): GoogleがReDoS対策として開発した線形時間エンジン

既存のスタックを変更できない場合でも、手順1~4を組み合わせることでリスクを大幅に下げられます。

中小企業でも今日からできること

情シス1人体制でも実践できる対策をまとめます。技術的な実装はエンジニアに依頼するとして、情シスが「依頼できる状態」を作ることが重要です。

・正規表現の使用箇所を棚卸しする: 開発チームに依頼し、入力バリデーションに正規表現を使っている箇所をリストアップしてもらう
・ライブラリの脆弱性を定期確認する: `npm audit`(Node.js)・`pip-audit`(Python)を定期実行してもらう。既知のReDoS CVEが含まれていないかの確認は自動化できる
・入力長制限を標準化する: フォームや APIの全入力フィールドに最大文字数制限を設けるよう開発ガイドラインに追記するだけで、新規開発時のリスクを大きく下げられる
・外部ライブラリの選定基準を設ける: バリデーション目的のライブラリ導入時に「ReDoS対応状況を確認する」をレビューチェックリストに加える

Linuxサーバー上でNode.jsやPythonアプリを動かしている場合、プロセス監視の設定も有効です。姉妹サイトLinuxMaster.JPでは、プロセスリソース管理やulimitを使ったCPU使用率の制限方法を解説しています。

よくある誤解と注意点

・「HTTPSにしているから大丈夫」は誤り: ReDoSはアプリケーション層の問題であり、TLSの有無とは無関係です
・「WAFが入っているから防げる」は誤り: WAFは不正なHTTPリクエストをブロックしますが、文法的に正しい(ただし細工された)文字列を含むリクエストは素通りします
・「すべての正規表現が危険」は誤り: `\d{3}-\d{4}` のようなシンプルな固定パターンは問題ありません。危険なのは量化子のネストや曖昧な選択肢を含むパターンだけです
・「100%安全なエンジンがある」は過信厳禁: DFAベースのエンジンでも一部の機能(後方参照等)には制限があり、導入前に動作確認が必要です

CVEの詳細は公式ソースで確認することを推奨します。NVD(https://nvd.nist.gov/)やJVN(https://jvn.jp/)で「ReDoS」または具体的なパッケージ名で検索できます。

ReDoS(正規表現DoS)とは?仕組み・被害例・対策をわかりやすく解説 - まとめ

本記事のまとめ

対策 効果 難易度
安全な正規表現パターンの記述 根本的な防止 低(知識があればすぐ対応可)
静的解析ツールでのスキャン 既存コードの洗い出し 低(ツール導入のみ)
入力文字列の長さ制限 被害の大幅な最小化 低(数行の実装)
タイムアウトの設定 被害の最小化 中(実装・テストが必要)
線形時間エンジンへの移行 根本的な防止 高(設計・移行コストが伴う)

ReDoSは「コードの書き方」が直接の原因となる脆弱性です。ファイアウォールやWAFといった外側の防御は補助にしかなりません。開発ガイドラインに安全な正規表現の記述ルールを組み込み、CIで静的チェックを自動実行することが、もっともコストパフォーマンスの高い長期対策です。

Webアプリケーション全体の脆弱性を体系的に学びたい方は、OWASP Top 10の解説記事もあわせてご覧ください。

PR

体系的に学ぶ 安全なWebアプリケーションの作り方 第2版(徳丸浩/SBクリエイティブ)

SQLインジェクション・XSSからReDoSまで、Webアプリの脆弱性を「なぜ起きるか」から丁寧に解説した定番書。開発チームへの勉強会資料としても活用できます。

関連記事をもっと読む

同じテーマの記事をまとめています。あわせて読みたい記事はこちらからご覧いただけます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次