
LLM APIトラフィック向けの透過的なPII編集プロキシ。アプリケーションとLLMプロバイダ(現在はAnthropic)の間に位置し、機密データを送信時に仮名化し、受信時に復元します。FastAPI + httpxで構築されています。
LLM APIトラフィック用の透過的PII編集プロキシ。アプリケーションとLLMプロバイダの間に位置し、送信時に機密データを仮名化し、戻り時に復元します。
LLMは実際の名前、メール、IP、ドメインを一切認識せず、[email protected] のような構造化された仮名でのみ動作します。アプリケーションは、元の値を透過的に受け取ります。
セキュリティ運用、インシデント対応、または実際の顧客データを含むタスクでLLMを使用する場合、PIIをサードパーティAPIに送信するリスクがあります。このプロキシは次の方法でその問題を解決します。
# 1. Create your config
cp config.json.example config.json
# Edit config.json with your internal domains, known entities, etc.
# 2. Run with Docker
docker build -t llm-token-proxy .
docker run -p 8090:8080 -v ./config.json:/app/config.json llm-token-proxy
# 3. Point your application at the proxy
export ANTHROPIC_BASE_URL=http://localhost:8090/session/my-session/
これだけです。Anthropic APIの呼び出しがPIIを編集した状態でプロキシを通るようになります。

通常のフロー: アプリケーション → トークンプロキシ(PII編集) → LLM API(仮名のみ) → トークンプロキシ(元の値を復元) → アプリケーション
[email protected] から admin)仮名はセッション内で決定論的です。同じ実際の値は常に同じ仮名にマッピングされます。
LLMがセキュリティログを分析する際、IPアドレスのホスティングプロバイダや地理位置情報は重要です。HetznerのドイツIPからのログインと、米国の住宅用ISPからのログインでは異なる意味を持ちます。ドキュメンテーション範囲のIP(例: 198.51.100.x)への単純な置換では、このコンテキストが失われます。
オプションの MaxMind GeoLite2-ASN データベースを使用すると、プロキシは実際のIPを 同じASNおよびサブネット内の別のIP に置き換えます。LLMは、実際のアドレスではありませんが、同じホスティングプロバイダとおおよその地理情報に解決される、実際に見えるIPを受け取ります。
10.99.99.x にマッピングされる(保持すべきASNコンテキストなし)198.51.100.x(ドキュメンテーション範囲)にフォールバックドナーIPは、セッションごとのソルトを使用したHMACによって決定論的に選択されるため、同じ実際のIPはセッション内では同じドナーにマッピングされますが、異なるセッションでは異なるマッピングが生成されます。
プロキシは空の config.json を同梱しています。組み込みの単語リストやドメイン固有の仮定はありません。付属の config.json.example は Microsoft SentinelとEntra IDを使用したセキュリティ運用(8,000以上のKQLテーブル/列名、Graph API許可用語、セキュリティ参照ドメイン)に合わせて調整されています。ユースケースが一致する場合は、必要な部分をコピーしてください。別のドメイン(医療、法律、金融など)でプロキシを使用する場合は、空の設定から始めて、独自のリストを作成してください。
config.json{
"internal_domains": ["yourcompany.com"],
"partner_domains": ["partnercorp.com"],
"internal_ip_ranges": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"],
"known_persons": ["John Smith"],
"known_orgs": ["YourCompany"],
"known_hostnames": ["DC01", "FS01"],
"ner_enabled": true,
"ner_skiplist": [],
"redaction_enabled": true
}
_internal_ 仮名になる)spacy + en_core_web_sm が必要)false の場合、プロキシは完全なパススルーになるfalse の場合、ドメインは変更されずに通過(メール、IP、名前は引き続き編集される)。LLMが重要なコンテキスト(例: outlook.com と protonmail.com の区別)を持ち、機密と見なされない場合に便利。再起動せずにホワイトリストを管理し、編集を切り替える:
# View all whitelists
curl http://localhost:8090/token-proxy/config/whitelist
# Add terms to NER skiplist (reduces false positives)
curl -X POST http://localhost:8090/token-proxy/config/whitelist \
-H "Content-Type: application/json" \
-d '{"category": "ner_skiplist", "values": ["EvoSTS", "Hetzner"]}'
# Add domains to allowlist (never pseudonymize these)
curl -X POST http://localhost:8090/token-proxy/config/whitelist \
-H "Content-Type: application/json" \
-d '{"category": "domain_allowlist", "values": ["github.com"]}'
# Disable redaction (pass-through mode)
curl -X POST http://localhost:8090/token-proxy/config/status \
-H "Content-Type: application/json" \
-d '{"redaction_enabled": false}'
ホワイトリストカテゴリ: ner_skiplist, domain_allowlist, known_persons, known_orgs, known_hostnames
プロキシがリアルタイムで何をしているかを検査:
# List active sessions
curl http://localhost:8090/token-proxy/sessions
# View pseudonym mappings for a session
curl http://localhost:8090/token-proxy/sessions/{session_id}/mappings
# View redaction activity log
curl http://localhost:8090/token-proxy/sessions/{session_id}/log
# Search mappings
curl http://localhost:8090/token-proxy/sessions/{session_id}/search?q=admin
# View captured payloads (what the LLM actually saw)
curl http://localhost:8090/token-proxy/sessions/{session_id}/payloads
# Token usage for a session (input/output tokens across all requests)
curl http://localhost:8090/token-proxy/sessions/{session_id}/usage
# Global statistics (includes total_tokens across all sessions)
curl http://localhost:8090/token-proxy/stats
プロキシは、転送するすべてのリクエストの input_tokens と output_tokens を記録します。ノンストリーミング(レスポンスの usage オブジェクトから読み取り)とストリーミング(message_start および message_delta SSEイベントから解析)の両方に対応しています。プロキシはアプリケーションとLLMの間に位置するため、共有プロキシを使用するすべてのクライアントの消費量を、個別に計装することなく単一のチェックポイントで測定できます。
curl http://localhost:8090/token-proxy/sessions/my-session/usage
# {
# "session_id": "my-session",
# "request_count": 3,
# "input_tokens": 1240,
# "output_tokens": 587
# }
curl http://localhost:8090/token-proxy/stats | jq .total_tokens
# { "input_tokens": 48213, "output_tokens": 19044 }
リクエストごとの使用量は /token-proxy/sessions/{session_id}/log の usage_counts にも含まれます。生のトークン数のみが追跡され、価格設定は呼び出し側に委ねられます。
プロキシはSSEストリーミング(stream: true)をサポートしています。仮名はテールバッファ方式を使用してリアルタイムで復元され、SSEチャンクに分割された仮名も処理できます。
プロキシはプロバイダアダプタパターンを使用しています。現在サポートされているのは以下です。
/v1/messages)他のプロバイダ(OpenAI、Google Geminiなど)のサポートを追加する方法については、CONTRIBUTING.md を参照してください。
en_core_web_sm)は英語の個人名/組織名を検出します。他の言語の名前は、設定の known_persons / known_orgs に追加されない限り見逃される可能性があります。admin [at] acme.com のような難読化されたメール、電話番号、住所)はキャッチされません。検出パイプラインは構造化されたIT/セキュリティデータ向けに調整されています。/token-proxy/config/* および /token-proxy/sessions/* エンドポイントには認証がありません。プロキシは信頼できる内部ネットワーク向けに設計されています。これらのエンドポイントを信頼できないネットワークに公開しないでください。# Install dev dependencies
pip install -e ".[dev,ner]"
python -m spacy download en_core_web_sm
# Run tests
pytest
# Lint
ruff check token_proxy/ tests/
Apache 2.0 — LICENSE を参照。
| エンティティタイプ | 内部例 | 外部例 |
|---|
| メール | [email protected] | [email protected] |
| ドメイン | domain-internal-001.com | domain-external-001.net |
| IP | 10.99.99.1(RFC1918) | ASN対応のドナーIP(後述) |
| 人物 | person_internal_001 | person_external_001 |
| 組織 | org_internal_001 | org_external_001 |
| ホスト名 | host_001 | host_001 |
| 変数 | デフォルト | 目的 |
|---|
ANTHROPIC_API_BASE | https://api.anthropic.com | 上流のAnthropic API URL |
TOKEN_PROXY_CONFIG_PATH | /app/config.json | 設定ファイルのパス |
LOG_LEVEL | info | ログレベル |
GEOIP_ASN_DB_PATH | /app/data/GeoLite2-ASN.mmdb | MaxMind GeoLite2-ASNデータベース(オプション) |