はじめに
障害調査で生成AIを使っていると、こんな場面があります。
2026-09-13 09:21:08 ERROR
user=tanaka@example.co.jp
client_ip=192.168.10.24
host=prd-app-01.example.local
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
Connection refused
原因を聞きたいだけなのに、ログにはメールアドレス、IPアドレス、ホスト名、認証情報候補などが混ざっています。
「全部消せば安全」ではありますが、消しすぎると今度は障害解析に必要な構造まで失われます。
この記事では、生成AIへログを貼る前に何をマスクし、何を残すかを、実務で使える形に整理します。
結論は単純です。
値そのものは隠し、同じ値であること・種類・ログの構造はできるだけ残す。
まず、生成AIへ渡す必要がある情報かを考える
マスキングは「何を送ってもよくする魔法」ではありません。
最初に確認すべきなのは、そのログを外部の生成AIサービスへ入力してよいかです。
会社・顧客・案件のルールで外部AIへの入力自体が禁止されている場合、マスキングツールを使ったからといって許可されるわけではありません。
また、利用するAIサービスの契約、データ保持、学習利用、管理者設定なども別の論点です。
その前提を満たしたうえで、入力内容から不要な識別情報を減らすのがマスキングです。
障害ログで最初に確認したい項目
私なら、少なくとも次を確認します。
| 種類 | 例 | 基本方針 |
|---|---|---|
| メールアドレス | tanaka@example.co.jp |
マスク |
| 電話番号 | 03-1234-5678 |
マスク |
| グローバル/プライベートIP | 192.168.10.24 |
原則マスク |
| APIキー・アクセスキー | AKIA... |
必ず除去・マスク |
| 顧客名 | Example株式会社 |
案件に応じてマスク |
| プロジェクト名 | Project-X |
案件に応じてマスク |
| 社内ホスト名/FQDN | prd-app-01.example.local |
必要性を確認 |
| ユーザー名 | tanaka |
必要性を確認 |
| エラーコード |
403, ECONNREFUSED
|
通常は残す |
| タイムスタンプ | 2026-09-13 09:21:08 |
原因分析に必要なら残す |
| ポート番号 |
443, 3389
|
通常は残す |
ポイントは、障害原因の推論に必要な情報と、実在する環境を特定する情報を分けることです。
例えば 192.168.10.24 という具体的なIPが必要なのではなく、「同じ接続元から同じ接続先へ通信している」ことだけが重要なケースは多くあります。
[MASKED] だけにしない
次のログを考えます。
10.0.1.10 -> 10.0.2.20:443 timeout
10.0.1.11 -> 10.0.2.20:443 timeout
10.0.1.10 -> 10.0.3.30:443 success
すべてを単純に [MASKED] にすると、
[MASKED] -> [MASKED]:443 timeout
[MASKED] -> [MASKED]:443 timeout
[MASKED] -> [MASKED]:443 success
となり、どのIPが同じものなのか分からなくなります。
そこで、値ごとに安定したトークンを割り当てます。
[IP_01] -> [IP_02]:443 timeout
[IP_03] -> [IP_02]:443 timeout
[IP_01] -> [IP_04]:443 success
これなら、実IPを渡さずに、
-
[IP_01]から[IP_02]は失敗 -
[IP_03]から[IP_02]も失敗 -
[IP_01]から別宛先[IP_04]は成功
という関係性を維持できます。
ネットワーク障害や認証障害の切り分けでは、この性質がかなり重要です。
マスク前後の具体例
マスク前
2026-09-13 09:21:08 ERROR
customer=Example株式会社
user=tanaka@example.co.jp
client_ip=192.168.10.24
server_ip=10.0.9.11
host=prd-app-01.example.local
port=443
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
error=Connection refused
マスク後
2026-09-13 09:21:08 ERROR
customer=[CUSTOM_01]
user=[EMAIL_01]
client_ip=[IP_01]
server_ip=[IP_02]
host=[CUSTOM_02]
port=443
AWS_ACCESS_KEY_ID=[AWS_KEY_01]
error=Connection refused
この形なら、port=443 や Connection refused といった解析材料は残しながら、環境固有値を減らせます。
正規表現だけでは足りない
メールアドレスやIPv4アドレスのように形式が明確なものは、パターン検出と相性がよいです。
一方で、次のようなものは形式だけでは判断しにくくなります。
Example株式会社
Project-Phoenix
prd-app-01
営業企画部
これらは単なる文字列です。
そのため実務では、
- メールアドレスやIPなどをパターンで検出
- 顧客名・案件名・社内ホスト名などを利用者が追加指定
- マスク結果を人間が確認
- その後に生成AIへ貼り付ける
という二段構えの方が現実的です。
自動検出だけを信用しないことが重要です。
マスキング処理そのものが外部送信していたら意味が薄い
もう一つ確認したいのが処理場所です。
「AIへ送る前にマスクする」ためのサービスなのに、マスキング対象の原文を別サーバーへ送って解析しているなら、送信先が一つ増えます。
用途によってはそれでも問題ありませんが、構成は理解しておくべきです。
私はこの点を避けるため、MaskedFormでは原文と置換対応表を現在のブラウザタブ内で処理する構成にしています。
現在の公開実装では、メールアドレス、日本の電話番号候補、IPv4アドレス、AWSアクセスキーID、決済カード番号候補を検出でき、顧客名やプロジェクト名などはセッション内のカスタム語句として登録できます。
同じ値には [EMAIL_01] や [IP_01] のような安定したトークンを割り当てます。
なお、MaskedFormはDLP製品ではなく、機密情報の完全検出を保証するものでもありません。最終確認は利用者側で必要です。
実務で使うなら、この順番
私なら次の手順にします。
1. 必要なログだけ切り出す
数千行をそのまま貼るのではなく、発生時刻前後や関連コンポーネントに絞ります。
2. 明確な識別情報をマスクする
メール、電話番号、IP、認証情報候補などを置換します。
3. 案件固有語を追加でマスクする
顧客名、案件名、内部ホスト名などです。
4. マスク後の文章を目視確認する
秘密鍵、Bearer Token、Cookie、独自形式のIDなど、自動検出対象外の値が残っていないか確認します。
5. AIに必要な前提を匿名化して追加する
例えば、
[IP_01] はクライアント、[IP_02] はWebサーバーです。
同時刻に別クライアントからも同じ事象が発生しています。
原因候補と追加確認コマンドを提示してください。
のように、実値ではなく役割を説明します。
この方が、情報を削りすぎるより回答精度を維持しやすくなります。
ブラウザ内で試す
この考え方を実際に試せるようにしたのが MaskedForm です。
登録なしで利用できます。
今回の記事専用に source=qiita-log-masking-20260913 を付けています。入力した原文や置換対応表を分析用イベントとして保存するのではなく、公開実装で記録するのは日付・イベント名・許可された区分・回数の集計です。
利用前には、所属組織や顧客との契約上、対象データを生成AIへ入力してよいかを必ず確認してください。
まとめ
生成AIへ障害ログを渡すときは、単に文字を消すのではなく、解析に必要な構造を残しながら実値を落とすのがポイントです。
特に意識したいのは、
- 認証情報は確実に除去する
- 個人・顧客・環境を特定する値は必要性を確認する
- 同じ値には同じトークンを割り当て、関係性を残す
- 正規表現で拾えない顧客名・案件名も確認する
- 自動マスキング後に必ず目視する
- そもそも外部AIへ送信可能な情報かを先に確認する
という点です。
生成AIは障害切り分けと相性がよい一方、入力前のひと手間を省く理由にはなりません。
「何を隠し、何を残すか」を決めてから使う方が、セキュリティと回答品質を両立しやすくなります。