はじめに
前回の記事では、LLM APIを使う前に知っておくべきセキュリティの基本を紹介しました。
今回は実践編です。実際にコードを書いて、プロンプトから個人情報(PII)を自動的に検出・除去し、LLMのレスポンスで元に戻す方法をハンズオンで学びます。
使うのは CloakLLM — オープンソースのPII保護ミドルウェアです。
なぜこれが必要なのか?
OpenAIやAnthropicにプロンプトを送ると、その内容はプレーンテキストでプロバイダーのサーバーに到達します。つまり:
"田中太郎様(メール: tanaka@example.com)の契約を要約してください"
このプロンプトをそのまま送ると、顧客の名前とメールアドレスがプロバイダーのログに残ります。
CloakLLMのアプローチ:
"[PERSON_0]様(メール: [EMAIL_0])の契約を要約してください"
LLMは [PERSON_0] を人名として理解できるので、レスポンスの品質は変わりません。でも実際のデータは外に出ません。
セットアップ
Python
pip install cloakllm
python -m spacy download ja_core_news_sm # 日本語NERモデル
Node.js
npm install cloakllm
ハンズオン①:基本のサニタイズ → LLM → デサニタイズ
Python版(10行)
from cloakllm import Shield, ShieldConfig
from openai import OpenAI
# 1. Shieldを初期化(日本語ロケール)
shield = Shield(ShieldConfig(locale="ja"))
# 2. 個人情報を含むプロンプト
prompt = "田中太郎様(メール: tanaka@example.com)の申請内容を要約してください"
# 3. サニタイズ — PIIをトークンに置換
sanitized, token_map = shield.sanitize(prompt)
# → "[PERSON_0]様(メール: [EMAIL_0])の申請内容を要約してください"
# 4. サニタイズ済みプロンプトをLLMに送信
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": sanitized}]
)
# 5. レスポンスの中のトークンを元の値に復元
restored = shield.desanitize(response.choices[0].message.content, token_map)
print(restored)
Node.js版
const { Shield } = require('cloakllm');
const OpenAI = require('openai');
const shield = new Shield({ locale: 'en' });
const client = new OpenAI();
const prompt = "Email john@acme.com about the meeting with Sarah Johnson";
// サニタイズ
const { sanitized, tokenMap } = shield.sanitize(prompt);
// → "Email [EMAIL_0] about the meeting with [PERSON_0]"
// LLMに送信
const response = await client.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: sanitized }]
});
// 復元
const restored = shield.desanitize(response.choices[0].message.content, tokenMap);
console.log(restored);
ハンズオン②:もっと簡単に — ワンライン統合
毎回 sanitize() / desanitize() を書くのは面倒ですよね。CloakLLMにはワンライン統合があります:
Python — OpenAI SDK
from cloakllm import enable_openai
from openai import OpenAI
client = OpenAI()
enable_openai(client) # これだけ!
# 以降、すべてのAPI呼び出しが自動的に保護される
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "田中太郎のメールはtanaka@example.comです"}]
)
# プロバイダーは実際のPIIを見ない
# レスポンスには元の値が自動復元される
Python — LiteLLM(100以上のプロバイダー対応)
import cloakllm
cloakllm.enable() # すべてのLiteLLM呼び出しが保護される
Node.js — OpenAI SDK
const cloakllm = require('cloakllm');
const OpenAI = require('openai');
const client = new OpenAI();
cloakllm.enable(client); // これだけ!
ハンズオン③:マイナンバーの検出
日本の開発者にとって重要なのが、マイナンバー(個人番号)の検出です。locale="ja" を設定すると、以下のPIIを自動検出します:
- 日本人の氏名(spaCy NERモデル使用)
- マイナンバー(12桁)
- 日本の電話番号(090/080/070、固定電話)
- 日本語のメールアドレス
from cloakllm import Shield, ShieldConfig
shield = Shield(ShieldConfig(locale="ja"))
text = "佐藤花子さん(マイナンバー: 123456789012、電話: 090-1234-5678)"
sanitized, token_map = shield.sanitize(text)
print(sanitized)
# → "[PERSON_0]さん(マイナンバー: [MY_NUMBER_JP_0]、電話: [PHONE_JP_0])"
print(token_map.entities)
# → {'PERSON': ['佐藤花子'], 'MY_NUMBER_JP': ['123456789012'], 'PHONE_JP': ['090-1234-5678']}
ハンズオン④:監査ログの確認
CloakLLMはすべてのサニタイズ操作をハッシュチェーンで記録します。改ざんがあれば検知できます。
# 監査チェーンの整合性を検証
python -m cloakllm verify ./cloakllm_audit/
# ✅ Audit chain integrity verified — no tampering detected.
# 統計情報を確認
python -m cloakllm stats ./cloakllm_audit/
# 何件のPIIが検出・保護されたかを確認
EU AI Act(2026年8月施行)のArticle 12は、高リスクAIシステムに改ざん防止ログを要求しています。このハッシュチェーンはその要件に対応しています。
ハンズオン⑤:暗号的証明(Ed25519署名)
「PIIを除去しました」と言うだけでなく、数学的に証明できます:
from cloakllm import Shield, ShieldConfig, DeploymentKeyPair
# Ed25519鍵ペアを生成
keypair = DeploymentKeyPair.generate()
# 署名付きShieldを初期化
shield = Shield(ShieldConfig(locale="ja", attestation_key=keypair))
# サニタイズ → 証明書が自動生成
sanitized, token_map = shield.sanitize("田中太郎のマイナンバーは123456789012です。")
# 暗号的に検証
cert = token_map.certificate
assert cert.verify(keypair.public_key) # True — 改ざんなし
この証明書には、入出力のSHA-256ハッシュ、タイムスタンプ、エンティティ数がEd25519で署名されています。
よくある質問
Q: トークンに置換するとLLMの回答品質は落ちますか?
A: ほとんどの場合、落ちません。LLMは [PERSON_0] を「ある人物」として理解し、文脈に沿った回答を生成します。固有名詞そのものが回答に必須なケース(例:「この人物の経歴を教えて」)では影響がありますが、要約・分析・翻訳などのタスクでは問題ありません。
Q: 対応しているPIIの種類は?
A: v0.4.0時点で36パターン、13ロケール対応。メール、電話番号、クレジットカード、SSN、IBAN、APIキー、IPアドレス、マイナンバー、パスポート番号など。
Q: ストリーミングレスポンスに対応していますか?
A: はい。v0.3.0からストリーミングデサニタイズに対応しています。
まとめ
| やりたいこと | コード |
|---|---|
| 基本のサニタイズ | shield.sanitize(text) |
| ワンライン統合(OpenAI) | enable_openai(client) |
| ワンライン統合(LiteLLM) | cloakllm.enable() |
| 日本語PIIの検出 | ShieldConfig(locale="ja") |
| 監査ログの検証 | python -m cloakllm verify ./ |
| 暗号的証明 | DeploymentKeyPair.generate() |
10行のコードで、顧客データがLLMプロバイダーに漏れるリスクをゼロにできます。
リンク
- GitHub: https://github.com/cloakllm/CloakLLM
- ドキュメント: https://cloakllm.dev
- PyPI:
pip install cloakllm==0.4.0 - npm:
npm install cloakllm@0.4.0 - 前回の記事: 新人プログラマがLLM APIを使う前に知っておくべきセキュリティの基本
MIT ライセンス | 527テスト | 13ロケール対応