【Java学習|初心者向け】Javaエンジニアなら知っておきたい!正規表現の落とし穴「ReDoS」を防ぐ基本と対策

1. 導入:なぜReDoSを知る必要があるのか

Java開発において、入力値のバリデーションやログ解析などで「正規表現」は非常に便利なツールです。しかし、書き方次第では「ReDoS(正規表現DoS攻撃)」という深刻な脆弱性を引き起こす可能性があります。これは、特定の文字列を処理する際に正規表現の計算量が爆発的に増え、サーバーのCPU使用率が100%に張り付き、システム全体が応答不能になる攻撃です。ユーザーからの入力値をそのまま正規表現に使う場合は、特に注意が必要です。

2. 基礎知識:正規表現の仕組みと「バックトラック」

Javaでは java.util.regex.Pattern クラスと java.util.regex.Matcher クラスを使って正規表現を扱います。
ReDoSが発生する主な原因は、正規表現エンジンが一致するパターンを探す過程で行う「バックトラック(Backtracking)」にあります。
例えば、`(a+)+$` という正規表現に対して `aaaaaaaaaaaaaaaaaaaaab` というような、最後までマッチしない長い文字列を与えるとします。正規表現エンジンは「どこかでマッチするはずだ」と試行錯誤を繰り返し、指数関数的な組み合わせを計算してしまいます。これがCPUを枯渇させる原因です。

3. 実装/解決策:脆弱なパターンの回避

ReDoSを防ぐための鉄則は「曖昧な繰り返しを重ねない」ことです。
入れ子の繰り返しを避ける: `(a+)+` のように、繰り返しの中にさらに繰り返しを含めない。
量指定子の多用を避ける: “ や `+` を過剰に使わず、可能な限り具体的な文字数や文字クラスを指定する。
タイムアウトを設ける: Java 9以降では、`Matcher` に直接タイムアウトを設定する機能はありませんが、正規表現の実行を別スレッドで行い、一定時間で打ち切るなどのガードが必要です。

4. サンプルプログラム

以下のコードは、ReDoSを引き起こしやすい「危険なパターン」と、それを回避する「安全なパターン」の比較です。

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ReDoSExample {
    public static void main(String[] args) {
        // 危険なパターン:(a+)+ は入力が長くなると計算量が爆発する
        String dangerousRegex = "(a+)+$";
        // 安全なパターン:具体的な文字数制限や構造を意識する
        String safeRegex = "a+$";

        String input = "aaaaaaaaaaaaaaaaaaaaab"; // 最後にマッチしない長い文字列

        System.out.println("検証開始...");
        
        // 危険なパターンの実行(非常に時間がかかる可能性がある)
        long start = System.currentTimeMillis();
        Matcher matcher = Pattern.compile(dangerousRegex).matcher(input);
        if (matcher.find()) {
            System.out.println("マッチしました");
        } else {
            System.out.println("マッチしませんでした");
        }
        long end = System.currentTimeMillis();
        
        System.out.println("処理時間: " + (end - start) + "ms");
    }
}

5. 応用・注意点:現場での防御策

現場でReDoSを防ぐための実践的なアドバイスをまとめます。

入力値の長さを制限する: 正規表現に渡す前に、入力文字列の最大長をバリデーションしてください。短い文字列であれば、バックトラックが発生しても被害は最小限に抑えられます。
Named Groupsを賢く使う: 正規表現が複雑になる場合は、名前付きグループ (?<name>…) を使用して可読性を上げ、論理的なミス(誤った繰り返し設定)を防ぎましょう。
ライブラリの活用: 複雑なパースが必要な場合は、正規表現に頼らず、専用のパーサーライブラリ(JSONならJacksonなど)を使用するのが最も安全です。

正規表現は強力ですが、強力なツールほど扱いには慎重さが必要です。特に外部からの入力値には「悪意があるかもしれない」という前提で実装することを心がけてください。

コメント

タイトルとURLをコピーしました