入力データマスキング / サニタイズ実践ガイド
本ガイドは AI エージェント/LLM に渡る前段での個人情報・機微データ・ソースコード断片などの不要曝露を低減し、誤用/再出力/リーク経路を遮断するための実装パターンを整理します。
🎯 目的
| 目的 | 説明 | 成功指標 |
|---|
| 最小送信 | 推論必要最小限フィールドのみ送信 | 平均送信フィールド数▼ / Redacted率▲ |
| 脱識別化 | 再識別困難な形へトークン化/ハッシュ化 | 再識別テスト成功率 < 1% |
| 動的マスキング | 文脈/権限に応じ差分マスキング | 権限外表示ゼロ |
| 出力再露出防止 | マスク済フィールド再出力禁止 | 再露出検出件数ゼロ |
🔍 リスク分類
| リスク | 例 | 影響 | 緩和 |
|---|
| PII直接送信 | 名前, メール, 住所 | 漏洩/再出力 | ハッシュ/トークン化 |
| 認証シークレット混入 | APIキー, Token | 認証悪用 | Secret検出 + ブロック |
| コード知財流出 | プロプライエタリ関数 | 競合利得 | 部分マスキング + 要約 |
| 内部ID相関 | 連番/UUID全送信 | 推論攻撃 | Surrogate Key変換 |
| 逆引き容易ハッシュ | SHA1/MD5単体 | 再識別 | Salt + KDF |
🧱 アーキテクチャ層
(Client) -> (Ingress Filter) -> (Masking Pipeline) -> (Policy Gate) -> (LLM)
|-> (Detokenization Service - scoped)
- Ingress Filter: MIME/サイズ/バイナリ拒否 - Masking Pipeline: PII検出 -> 置換 -> Token Map保存 - Policy Gate: 権限/利用目的/データ分類適合性検証 - Detokenization: 最小権限 + 監査ログ必須🧪 PII/Secret 検出ルール例 (Python)
import re
PII_PATTERNS = {
'email': re.compile(r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}"),
'phone': re.compile(r"\b\+?\d[\d -]{8,}\d\b"),
'name_like': re.compile(r"\b[A-Z][a-z]+\s[A-Z][a-z]+\b"),
}
SECRET_PATTERNS = {
'api_key': re.compile(r"sk-[A-Za-z0-9]{32,}"),
'aws_key': re.compile(r"AKIA[0-9A-Z]{16}"),
}
def detect(text: str):
findings = []
for label, pat in {**PII_PATTERNS, **SECRET_PATTERNS}.items():
for m in pat.finditer(text):
findings.append({'type': label, 'span': m.span(), 'value': m.group(0)})
return findings
🔐 マスキング戦略
| 戦略 | 手法 | 再識別リスク | 用途 |
|---|
| 固定トークン | 置換 | 低 | 一般会話/要約 |
| ハッシュ(SHA256+Salt) | digest表示 | 中 (頻度低) | ログ痕跡 |
| Format-Preserving Mask | 部分保持(**1234) | 中 | UX表示 |
| 属性汎化 | 年齢→年代 | 低 | 統計/分析 |
| 合成データ置換 | Faker生成 | 最低 | テスト/検証 |
🧬 動的ポリシー例 (YAML)
version: 1
rules:
- id: deny_raw_secret
match: secret
action: block
- id: pii_email
match: email
action: mask_token
- id: pii_name
match: name_like
action: generalize
- id: code_block
match: code
action: summarize
🔁 双方向トークナイズ
token_map = {}
def tokenize(value: str) -> str:
import secrets
token = f"TKN_{secrets.token_hex(8)}"
token_map[token] = value
return token
def detokenize(token: str, actor_role: str) -> str:
if actor_role != 'auditor':
raise PermissionError('not allowed')
return token_map.get(token, '')
✅ 検証指標
| 指標 | 計測方法 | 目標 |
|---|
| Redaction率 | (検出PII中マスク適用割合) | > 98% |
| False Positive率 | 手動サンプル再注釈 | < 5% |
| 逆引き成功率 | レインボーテーブル攻撃テスト | < 1% |
| 秘匿漏洩再出力数 | 監査ログ集計 | 0 |
| 遅延オーバーヘッド | p95処理時間比較 | < +30ms |
🚀 導入ステップ
- 現行ログ/入力フィールド棚卸し & 機微分類
- PII + Secret 検出 PoC (再現率/精度測定)
- Token化 + 再出力禁止ルール (Regressionテスト追加)
- Detokenization API 最小権限 + 監査ログ化
- 本番段階的ロールアウト (Shadow → Enforce)
- 継続評価: 指標ダッシュボード & 月次レビュー
🧭 関連
- ソースコード漏洩防止: ./source-code-leak-prevention.md
- 監査ログ: ./audit-logging.md
- プロンプトインジェクション: ./prompt-injection.md
戻る: ./index.md