正規表現(Regex)完全攻略

解析

サイバーセキュリティの現場で使われるパターンマッチングの技術——
個人情報検出・ログ解析・インジェクション防御まで徹底解説

カテゴリ: セキュリティ基礎

読了目安: 約15分

難易度: ★★★☆☆

正規表現とは何か

正規表現(Regular Expression / Regex)とは、文字列のパターンを記述するための記法です。1950年代に数学者スティーヴン・クリーネが形式言語理論の一部として定義し、その後UNIXツールに実装されて普及しました。現在ではほぼすべてのプログラミング言語・セキュリティツール・SIEM(セキュリティ情報イベント管理システム)に組み込まれています。

サイバーセキュリティの文脈では、正規表現は攻撃ツールにも防御ツールにもなります。侵入者はSQLインジェクションのペイロードをバイパスするために正規表現の脆弱性を突き、防御者はSIEMやWAF(Web Application Firewall)でパターンマッチングにより脅威を検知します。

  • ReDoS:不正な正規表現によるサービス拒否攻撃。2022年に複数のCVEが登録された
  • ~70%:WAFルールのうち正規表現ベースで記述されている割合(OWASP推計)
  • O(2ⁿ):バックトラック爆発が起きた場合の最悪計算量。数十文字で数時間かかることもバックトラック爆発が起きた場合の最悪計算量。数十文字で数時間かかることも
  • PCRE:Perl Compatible Regular Expressions。業界標準として最も広く使われる実装

基本構文リファレンス

正規表現の構文は方言によって微妙に異なりますが、PCREを基準とした以下の要素が現場で最もよく使われます。セキュリティツールのルール記述や、Pythonのreモジュール・JavaScriptのRegExpでそのまま応用できます。

メタ文字意味マッチ例
.任意の1文字(改行除く)a.cabc, a9c, a-c
^行頭^Error行頭が “Error” の行
$行末\.log$access.log など
*直前の要素を0回以上ab*cac, abc, abbc
+直前の要素を1回以上ab+cabc, abbc(acは不可)
?直前の要素を0or1回colou?rcolor, colour
{n,m}n回以上m回以下\d{4,6}1234, 12345
[...]文字クラス[a-zA-Z]英字1文字
[^...]否定文字クラス[^0-9]数字以外の1文字
(…)グループ化・キャプチャ(\d{3})-(\d{4})グループとして参照可能
(?:…)非キャプチャグループ(?:foo|bar)foo か bar
\d数字 [0-9]\d+1, 42, 9999
\w単語文字 [a-zA-Z0-9_]\w{3,}abc, foo_bar
\s空白文字(スペース・タブ等)\s+空白1文字以上
\b単語境界\bcat\bcatはマッチ、concatenateは不可
(?=…)肯定先読み\d(?=px)“10px” の “10”
(?!…)否定先読みfoo(?!bar)foobarの前のfooは不可

日本固有の個人情報パターン — 完全リファレンス

以下は日本で実際に流通する個人情報・金融情報のフォーマットに合わせた正規表現集です。PCI DSS・個人情報保護法(改正2022年施行)対応のDLP(データ損失防止)ツールやSIEMルールに直接応用できます。各パターンは実際のフォーマット仕様に基づいており、誤検知を抑えるよう設計しています。

種別正規表現パターンマッチ例備考・仕様根拠
▸ 電話番号
固定電話(全国)0\d{1,4}-\d{1,4}-\d{4}03-1234-5678
0120-12-3456
市外局番は1〜4桁。全体10〜11桁
携帯電話0[789]0-\d{4}-\d{4}090-1234-5678
080-9876-5432
070/080/090の3種のみ。総務省告示に基づく
フリーダイヤル0120-\d{3}-\d{3}0120-123-456NTTコミュニケーションズ仕様
ハイフンなし電話0\d{9,10}09012345678DB格納・ログ形式で頻出
▸ 郵便番号
標準形式[〒]?\d{3}-\d{4}〒150-0001
100-8994
〒プレフィックスは任意。日本郵便仕様
ハイフンなし\d{7}1500001APIやシステム間連携で使われることが多い
▸ クレジットカード番号
VISA4\d{3}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}4111-1111-1111-1111先頭4。16桁。PAN検出の基本
Mastercard5[1-5]\d{2}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}5500 0000 0000 0004先頭51〜55。16桁
American Express3[47]\d{2}[- ]?\d{6}[- ]?\d{5}3714 496353 98431先頭34/37。15桁。4-6-5の区切り
JCB35(?:2[89]|[3-8]\d)\d{12}3566002020360505先頭3528〜3589。16桁。日本発祥のブランド
セキュリティコード(CVV)(?:CVV|CVC|CVV2)[:\s]*(\d{3,4})CVV: 123ログへの記録はPCI DSS違反
▸ 銀行口座番号
金融機関コード\d{4}0001(みずほ)
0009(三井住友)
全国銀行協会(全銀)仕様。4桁固定
支店コード\d{3}001, 012, 9993桁固定。全銀フォーマット準拠
普通・当座口座番号\d{7}12345677桁固定。ゆうちょは8桁の場合もある
ゆうちょ記号番号\d{5}-\d{8}10000-12345678記号5桁+番号8桁。ゆうちょ独自仕様
全銀フォーマット(振込用)\d{4}-\d{3}-\d{1}-\d{7}0001-001-1-1234567金融機関-支店-預金種別-口座番号
▸ マイナンバー・識別番号
マイナンバー(個人)\d{4}[- ]?\d{4}[- ]?\d{4}1234 5678 901212桁。番号法(2013年)に基づく。下1桁はチェックデジット
法人番号[1-9]\d{12}123456789012313桁。先頭は1〜9。国税庁が付番
運転免許番号\d{2}-\d{2}-\d{6}12-34-567890都道府県コード2桁+年2桁+連番6桁(一般的形式)
パスポート番号[A-Z]{2}\d{7}TK1234567アルファベット2文字+数字7桁。ICAO規格準拠
▸ 日本人の名前(氏名)
漢字氏名(姓名の間にスペース)[\u4E00-\u9FFF]{1,5}\s[\u4E00-\u9FFF]{1,5}山田 太郎漢字のみ。スペース区切り。各1〜5文字
ひらがな氏名[\u3040-\u309F]{1,8}[\s ][\u3040-\u309F]{1,8}やまだ たろうフリガナ欄のひらがな。半角・全角スペース両対応
カタカナ氏名[\u30A0-\u30FF]{1,8}[\s ][\u30A0-\u30FF]{1,8}ヤマダ タロウフリガナ・外来語名前。旧字対応は要拡張
ローマ字氏名(姓名)[A-Z][a-z]{1,10}\s[A-Z][a-z]{1,10}Taro Yamadaパスポート・国際フォーマット準拠
▸ ネットワーク・認証情報
メールアドレス[\w.+\-]+@[\w\-]+\.[\w.\-]+user@example.co.jpRFC5321準拠の簡略版。完全準拠はさらに複雑
IPv4アドレス(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)192.168.1.10〜255の範囲チェック込み
JWTトークンeyJ[A-Za-z0-9_\-]+\.eyJ[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+eyJhbGci…ログ・コード中のJWT漏洩検出に使用
AWSアクセスキー(?:A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z2-7]{16}AKIAIOSFODNN7EXAMPLEGitHubシークレットスキャンでも使用されるパターン

⚠️

注意 — Luhnアルゴリズムとの組み合わせ

クレジットカード番号の正規表現はフォーマット検証のみであり、実在する番号かどうかは判定できません。実際のDLPやフォーム検証ではLuhnアルゴリズム(モジュロ10チェックデジット)との組み合わせが必須です。正規表現のみでの運用は誤検知率が高くなります。

実装例:Python DLPスキャナー

以下は上記パターンを使ったシンプルなDLP(データ損失防止)スキャナーの実装例です。ログファイルやテキストデータから個人情報を検出し、アラートとマスキングを行います。

import re
from dataclasses import dataclass
from typing import List, Dict

# ── 検出パターン定義 ──────────────────────────────
PATTERNS: Dict[str, str] = {
    "携帯電話番号":     r"0[789]0[-\s]?\d{4}[-\s]?\d{4}",
    "クレジットカード":  r"4\d{3}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}",
    "マイナンバー":     r"\d{4}[-\s]\d{4}[-\s]\d{4}",
    "郵便番号":         r"[〒]?\d{3}-\d{4}",
    "メールアドレス":   r"[\w.+\-]+@[\w\-]+\.[\w.\-]+",
    "AWSキー":          r"(?:AKIA|AGPA|AIDA)[A-Z2-7]{16}",
}

def scan_text(text: str) -> List[Dict]:
    """テキスト中の個人情報パターンを検出してレポートを返す"""
    findings = []
    for label, pattern in PATTERNS.items():
        for m in re.finditer(pattern, text):
            findings.append({
                "type":    label,
                "value":   m.group(),
                "start":   m.start(),
                "end":     m.end(),
            })
    return findings

def mask_text(text: str) -> str:
    """検出したパターンを *** でマスク(ログ安全化)"""
    for label, pattern in PATTERNS.items():
        text = re.sub(pattern, lambda m: "[MASKED:" + label + "]", text)
    return text

# ── 使用例 ────────────────────────────────────────
sample = "顧客連絡先: 090-1234-5678 カード: 4111-1111-1111-1111"
for f in scan_text(sample):
    print(f"[ALERT] {f['type']} detected: {f['value']}")
print(mask_text(sample))
# → [ALERT] 携帯電話番号 detected: 090-1234-5678
# → [ALERT] クレジットカード detected: 4111-1111-1111-1111
# → 顧客連絡先: [MASKED:携帯電話番号] カード: [MASKED:クレジットカード]

ReDoS — 正規表現を悪用した攻撃

ReDoS(Regular Expression Denial of Service)は、悪意あるユーザーが脆弱な正規表現に対して精巧に細工された入力を与えることで、サーバーのCPUを100%占有させるサービス拒否攻撃です。OWASP Top 10の「A05:2021 Security Misconfiguration」に関連するリスクです。

🔴 ATTACK SCENARIO — ReDoS実例

2019年、人気Node.jsパッケージ ua-parser-js(週間ダウンロード数700万超)でReDoS脆弱性(CVE-2022-25927)が発見されました。User-Agentヘッダーの解析に使われていた正規表現がカタストロフィックバックトラッキングを起こし、50文字程度の悪意ある文字列でサーバーを数秒間停止させることができました。

また2016年にはStack Overflowが脆弱な正規表現への攻撃で34分間ダウンするインシデントが発生しています(事後報告書で公開済み)。

脆弱なパターンの見分け方

ReDoSの主な原因はネストされた量指定子(Nested Quantifiers)重複した代替選択(Ambiguous Alternation)です。以下のような構造には注意が必要です。

# ❌ 脆弱 — ネストされた量指定子
^(\d+)+$       # "111111111111111X" で指数的バックトラック
^(a+)+$        # 古典的なReDoS例
([a-zA-Z]+)*   # アンビギュアスな繰り返し

# ✅ 安全な代替 — アトミックグループ or 所有量指定子 (PCRE)
^(\d++)$        # 所有量指定子(++)でバックトラック禁止
^(?>\d+)$      # アトミックグループ

# ツール: cargo fuzz / regexploit / vuln-regex-detector で検査可能

🔴

重要 — ユーザー入力を正規表現に組み込む場合

ユーザーが入力した文字列をそのまま正規表現のパターンに組み込む(動的正規表現)ことは、ReDoS以外にもRCE(リモートコード実行)の危険性があります。必ずre.escape()(Python)やRegExp.escape()でサニタイズし、タイムアウト制限を設けてください。

WAF・SIEMでの実践的なルール記述

ModSecurity(OWASP CRS)やSplunkなどのSIEM製品では、正規表現ベースの検知ルールが中核を担います。以下はSQLインジェクション・XSS・ログインブルートフォースの検知パターン例です。

脅威カテゴリ検知パターン対象フィールド誤検知対策
SQLi(UNION SELECT)(?i)\bunion\b.{0,50}\bselect\bURI, Body大文字小文字無視フラグ必須。正規コンテンツに”union”が含まれる場合は除外リスト
XSS(スクリプトタグ)(?i)<\s*script[^>]*>Body, HeadersHTMLエンコード後の入力も検査。%3Cscript など
パストラバーサル(?:\.\.[\\/]){2,}URIURLデコード後に検査することが重要
コマンドインジェクション(?:;|\|{1,2}|&&)\s*(?:cat|ls|id|whoami|wget|curl)POST Bodyシェルメタキャラクタ+コマンド名の組み合わせ
ブルートフォース(Splunk)failed\s+(?:password|login)\s+for\s+\S+\s+from\s+([\d.]+)syslog5分以内に同一IPから10件以上 → アラート
Log4Shell(CVE-2021-44228)\$\{(?:j|J)(?:n|N)(?:d|D)(?:i|I):全ヘッダー大文字小文字混在の難読化に注意。OWASP CRS 3.3.4で対応済み

パフォーマンスチューニング — 本番環境での注意点

セキュリティルールの正規表現は高頻度で実行されるため、パフォーマンスが直接レイテンシに影響します。以下は現場で使われる最適化テクニックです。

最適化のベストプラクティス

① コンパイルキャッシュ — Pythonならre.compile()で事前コンパイルし、ループ外でオブジェクトを再利用。大量処理で最大3〜5倍の高速化が見込めます。

② 文字クラスの具体化 — \wより[a-zA-Z0-9_]のほうが一部のエンジンでは高速です。Unicode対応が不要なら明示することで検査範囲を絞れます。

③ アンカーの活用 — 行頭^・行末$アンカーを使うことでマッチング試行位置を制限し、不要なバックトラックを削減できます。

④ DFA型エンジンの採用 — RE2(Google製)やHyperscan(Intel製)はNFA型(PCRE)と異なりバックトラックを行わないため、ReDoSが構造的に発生しません。大規模ログ処理にはHyperscanが有効です。

まとめ — 正規表現をセキュリティの武器にするために

正規表現は単なる文字列処理ツールではなく、DLP・SIEM・WAF・コードスキャンなど現代のセキュリティインフラの根幹をなす技術です。本記事で紹介したパターンはそのまま実務に活用できますが、以下の点を必ず意識してください。

第一に、正規表現は形式検証であり意味検証ではありません。マイナンバーのチェックデジット検証やLuhnアルゴリズムなど、ビジネスロジックと組み合わせて初めて実用的な検証になります。第二に、ユーザー入力の動的組み込みは原則禁止とし、エスケープ処理とタイムアウト設定を徹底してください。第三に、本番環境への投入前には必ずReDoS検査ツール(vuln-regex-detector、regexploit等)でパターンを検証する運用フローを確立してください。

日本のプライバシー法制(改正個人情報保護法・番号法)に対応したDLPルールの整備は今後ますます重要になります。本記事のパターンを出発点に、自社環境のログ形式や業務データに合わせてカスタマイズしていただければ幸いです。

📚

参考リソース

OWASP ModSecurity CRS(coreruleset.org)、NIST SP 800-92(ログ管理ガイドライン)、個人情報保護委員会「個人情報の保護に関する法律についてのガイドライン」、PCI DSS v4.0(クレジットカードセキュリティ要件)、CVE-2022-25927(ua-parser-js ReDoS)を参照してください。

コメント