Inference Hooks:Claude Enterprise 向けのインラインDLP
- Inference Hooks の実体 — Anthropic から自組織で運用するサーバーへの HTTPS POST であり、WebSocket でもオンデバイスエージェントでもない
- プロンプトフレームのスキーマ — AI セキュリティサーバーが見るもの(そして決して見ないもの:システムプロンプト、隠された推論、生バイト)
- 判定 JSON:allow、deny_reason 付きの deny、そして今日あえて redact アクションが存在しない理由
- 署名モデル — Standard Webhooks HMAC-SHA256、すべての初回統合を捕まえる 2 つの検証バグ、そして whsec_ シークレット形式
- ユーザーがブロックされるか、モデルが未検査のトラフィックを受け取るかを決める 3 つの運用レバー:判定タイムアウト、失敗時ハンドリング、サーキットブレーカー
- 初日に爆発しない展開プレイブック — シャドウモード → パーセンテージロールアウト → ロール除外 → 施行、この順で
2026 年 8 月 5 日に発表された Inference Hooks は、Claude Enterprise のシートを展開したあとに必ずセキュリティチームが尋ねる問い — 規制対象データを含むプロンプトがモデルに到達すること自体を、どうやって止めるのか? — に対する Anthropic のファーストパーティの回答です。これまでの答えは、claude.ai への TLS トラフィックを傍受する企業プロキシ — 脆くて、不完全で、Claude Code CLI にはまったく効かないもの — でした。Inference Hooks は、その施行地点を Anthropic のパリメーターの内側に移します:ガバナンス対象のプロンプトごとに、Anthropic は推論を一時停止し、トランスクリプトを自組織が運用するサーバーに POST し、モデルが何かを見る前に allow または deny を待ちます。
一段落バージョン
自組織で HTTPS エンドポイントを立てます。Anthropic はガバナンス対象のすべてのプロンプトを署名付き POST(Standard Webhooks HMAC-SHA256)として送ります。サーバーが {"action": "allow"} を返せば推論が続行し、{"action": "deny", "deny_reason": "..."} を返せばユーザーは理由を見て、プロンプトは決してモデルに到達しません。エンドポイントは 1 つの設定で Claude Enterprise チャット、Claude Code、Cowork をカバーします。サーバーがタイムアウトしたり 500 を返した場合、失敗時ハンドリングの設定が、リクエストをブロックするか未検査で通すかを決めます。判定の施行をオンにする前に、シャドウモード + パーセンテージロールアウト + ロール除外で段階的に展開してください。
Inference Hooks vs Compliance API
どちらも同じ相手 — Claude Enterprise のセキュリティ、リーガル、コンプライアンスチーム — のために存在しますが、リクエストのライフサイクルの両端で動きます。
| Inference Hooks | Compliance API | |
|---|---|---|
| タイミング | インライン、推論が走る前 | 事後 |
| 役割 | ガバナンス対象の各リクエストをリアルタイムに許可または拒否 | 監査・エクスポート用のアクティビティ、チャット、ファイル、プロジェクト、ユーザーを取得 |
| 方向 | Anthropic → 自組織のサーバー | 自組織 → Anthropic |
| 目的 | 漏洩を止める | 何が起きたかを証明する |
多くの企業は両方を運用します。Hooks は仕掛け線、Compliance API は監査ログです。
判定のラウンドトリップの流れ
- 対象は claude.ai チャット、Claude Code(Web、デスクトップ、CLI)、Claude Cowork。会話タイトル生成のような付随リクエストは送信されません。音声モードはベータの対象外です。
- 管理者が設定した URL へ 1 回の HTTPS POST。ヘッダーには Content-Type: application/json、User-Agent: anthropic-dlp/1、そして 3 つの Standard Webhooks 署名ヘッダー(webhook-id、webhook-timestamp、webhook-signature)が含まれます。
- base64 デコードした whsec_ シークレットで、`{webhook-id}.{webhook-timestamp}.{生ボディバイト}` に対して HMAC-SHA256 を計算。時計とのずれが 5 分を超えるもの、または署名が一致しないものはすべて拒否してください。
- {"action": "allow"} または {"action": "deny", "deny_reason": "..."} のいずれか。Anthropic はレスポンスボディを最大 64 KiB まで読み、リダイレクトは追いません。
- allow の場合、推論は通常どおり進みます。deny の場合、ユーザーは deny_reason に続いて管理者が設定した固定メッセージを見ることになり、モデルはプロンプトを見ません。すべての拒否は組織のアクティビティフィードに記録されます。
Anthropic のサーバー上で動かす(ユーザーの端末上ではなく)ことの要点は 均一性 にあります:1 つの設定、1 つのサーバー、そしてあらゆるサーフェスのガバナンス対象リクエストがすべて同じ方法で検査されます。従業員のノート PC にインストールするものはなく、アプリごとの統合を同期させ続ける必要もありません。
プロンプトフレーム
各リクエストは、以下のトップレベルフィールドを持つ JSON ボディです:
| フィールド | 型 | 説明 |
|---|---|---|
type | string | 現状は常に "prompt"。将来的に新しいイベントタイプが登場します — サーキットブレーカーに引っかからないよう、認識できない値には allow を返してください。 |
request_id | string | 推論呼び出しごとの不透明な識別子。webhook-id ヘッダーと一致します — べき等性キーとして使ってください。 |
tenant_id | string | null | 組織用の不透明な識別子。 |
actor | object | type で判別("user" が現状の唯一の値)。ユーザーのリクエスト間で安定するタグ付き id と、可能な場合の email_address を持ちます。どちらも null になり得ます。 |
source | object | {"application": "..."}。既知の値:claude-ai、claude-code、config-test(管理者の「接続テスト」ボタンが使用)。オープン列挙型 — 新しい値が登場します。 |
session_id | string | null | 会話の不透明な識別子。パースしないでください。Claude Code ではベストエフォート。 |
model | string | null | 可能な場合、このリクエストの公開モデル識別子。 |
messages | array | 推論時点までの会話トランスクリプト — 下の「コンテンツブロック」を参照。 |
metadata | object | 予約済みの拡張マップ。今日は空。知らないキーには寛容に。 |
最小限のライブリクエストは次のような形です:
{
"type": "prompt",
"request_id": "req_abc123",
"tenant_id": "11111111-1111-1111-1111-111111111111",
"actor": {
"type": "user",
"id": "user_01AbCdEfGhIjKlMnOpQrStUv",
"email_address": "alice@example.com"
},
"source": { "application": "claude-ai" },
"session_id": "22222222-2222-2222-2222-222222222222",
"model": "claude-sonnet-5",
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Summarize the attached report." },
{
"type": "attachment",
"file_name": "q2-report.pdf",
"media_type": "application/pdf",
"size_bytes": 48213,
"text": "Q2 revenue grew 14% quarter over quarter..."
}
]
}
],
"metadata": {}
}
コンテンツブロック
各 messages[].content[] エントリは type を持ち、公開されている Messages API のコンテンツモデルに一致します。ツール結果は user ロールの下に現れます。
ブロック type | フィールド |
|---|---|
text | text |
tool_use | id、tool_name、input |
tool_result | content(改行で結合されたテキスト。バイナリ部分はプレースホルダーマーカー)、is_error、tool_name、tool_use_id |
attachment | file_name、media_type、size_bytes、text(抽出テキスト、トランスクリプト、またはリンクメタデータ) |
トランスクリプトに決して含まれないもの
これはプライバシーレビューで引っかかりやすい部分です。
- システムプロンプトなし。 Anthropic のもの、あなたのもの(プロジェクト/スキル経由)、モデルの憲法 — いずれも送信されません。
- 隠された推論なし。 Claude の拡張思考チェーンは、サーバーが見るトランスクリプトの一部ではありません。
- ツール定義なし。 呼び出しとその結果だけ。
- 生バイトなし。 ファイルや画像はメタデータと抽出テキストで表現されます。画像のみのコンテンツ(ドキュメントのスクリーンショットなど)は検査されません。
- Anthropic 内部のコンテキストやトラストバウンダリは含まれません。
トランスクリプトは エンドユーザーから見た会話 とツールのトレースです。含まれる内容がすべて除外されるブロックやターンはまるごと落とされるため、パース時に user/assistant の厳密な交互を仮定してはいけません。
ひとつだけあるサイズの落とし穴
トランスクリプトは切り詰めなしで 10 MB の上限まで送られます。よくあるデフォルトはそれよりずっと小さい — nginx の client_max_body_size は 1 MB、Express の express.json() は 100 kB、多くの PaaS リバースプロキシは数 MB でキャップされます。サーバーが拒否するボディは Webhook 失敗となり、リクエストを許可する失敗時ハンドリングでは、そのサイズ超過のプロンプトが 未検査のままモデルに届きます。施行前にボディ上限を上げてください。
判定スキーマ
どちらの結果でも HTTP 200 で応答してください。action フィールドで判別します。
Allow:
{ "action": "allow" }
Deny:
{
"action": "deny",
"deny_reason": "This prompt appears to contain customer payment card data, which your organization's policy does not allow.",
"reference_id": "scan_01HXPT4R9V"
}
| フィールド | 型と上限 | 意味 |
|---|---|---|
action | "allow" または "deny"、必須 | allow は推論を続行させます。deny はリクエストを拒否します。 |
deny_reason | 文字列または null、最大 500 文字、超過分は切り詰め | エンドユーザーに表示され、管理者が設定した固定メッセージに追記されます。スキャナールールの名前ではなく、ユーザーに何を変えればよいかを伝える文言を書いてください。 |
reference_id | 文字列または null、最大 50 文字、[A-Za-z0-9._:/-] から | 自分側での評価識別子。拒否時のアクティビティフィードエントリ inference_hooks_request_denied に記録されますが、エンドユーザーには表示されません。不透明に — リクエスト内容や個人データを含めないでください。 |
なぜ redact アクションが存在しないのか
判定はあえて二値です。Anthropic は {"action": "redact", "rewritten_prompt": "..."} を追加して DLP サーバーにトランスクリプトを飛行中にサニタイズさせることもできましたが、それは Anthropic があなたの箱が返したものを組織の権限のもとにモデルへ送ることを意味します。この設計はそのトラストバウンダリを鋭く保っています:サーバーはコンテンツを 評価 するのであり、作成 するのではありません。マスキングが必要なら、ユーザーが送信ボタンを押す前のクライアント側で行ってください。
形式のせいで deny が捨てられることはない
サイズ超過の deny_reason は切り詰められ、不正な reference_id は黙って捨てられますが、action は依然として尊重されます。逆は成り立ちません:HTTP 200 かつ解析可能な判定以外はすべて Webhook 失敗であり、deny ではありません。ブロックを HTTP 403 で示すと、あなたの deny は静かにフェイルオープンな allow(または failure handling によってはブロック)に化け、そのすべてがサーキットブレーカーにカウントされます。
最小の動くサーバー
12 行の Python で作る全許可 AI セキュリティサーバー
# Run with: python server.py — expose on an https:// URL your admin configures.
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
class VerdictHandler(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1" # keep the connection open between verdicts
def do_POST(self):
self.rfile.read(int(self.headers.get("Content-Length", 0)))
verdict = b'{"action": "allow"}'
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(verdict)))
self.end_headers()
self.wfile.write(verdict)
ThreadingHTTPServer(("", 8000), VerdictHandler).serve_forever()これを 443 番ポートの TLS 終端リバースプロキシの後ろに置き、エンドポイントとして設定し、管理コンソールで 接続テスト を押せば、allow 判定が確認できます。これはまさにアーカイブ専用統合の形です:無条件に allow を返し、応答後にフレームを永続化する — Compliance API のポーリングに対するプッシュ型の代替です。ただし、これは施行に使ってはいけません — 未署名のものを含めすべてのリクエストを受け入れてしまいます。判定の施行 をオンにする前に、必ず署名検証を追加してください。
署名検証
署名は Standard Webhooks 仕様に従います。3 つのヘッダー。Anthropic が送るときは小文字ですが、参照は大文字小文字を区別しません(プロキシで大文字化されます)。
| ヘッダー | 内容 |
|---|---|
webhook-id | 配信ごとに一意。ボディの request_id と一致します。べき等性キーとして使ってください。 |
webhook-timestamp | Unix 時刻(秒)を 10 進数文字列で。時計とのずれがいずれかの方向に 5 分 を超えるものは拒否 — それがリプレイウィンドウです。 |
webhook-signature | 空白区切りの v1,<base64> の値。それぞれがバイト列 {webhook-id}.{webhook-timestamp}.{生ボディバイト} に対する HMAC-SHA256 です。いずれかの値が自分の計算と一致すれば受け入れる — 定数時間比較を使ってください。 |
初回統合を必ず捕まえる 2 つのバグ
- 生バイトを検証し、再エンコードしたJSONを検証しないこと。 パースや再シリアライズの前に、受け取ったままのボディに対してHMACを計算してください。json.loads() → json.dumps() のラウンドトリップは空白を変えてしまい、ここで死にます。
- シークレットは URL セーフではなく、STANDARD な base64 デコーダでデコードすること。 署名シークレットは whsec_ プレフィックスの後ろの値で、標準アルファベット(+ と /)でエンコードされています。URL セーフデコーダは、シークレットに + や / が含まれるとき(つまりほとんどの場合)に間違った鍵バイトを導きます — そして失敗は無音の定数時間ミスマッチです。
参考実装(Anthropic のドキュメントから圧縮):
import base64, hashlib, hmac, time
TOLERANCE_SECONDS = 300
def verify(secret: str, headers: dict[str, str], body: bytes) -> bool:
h = {k.lower(): v for k, v in headers.items()}
try:
msg_id, ts, sigs = h["webhook-id"], h["webhook-timestamp"], h["webhook-signature"]
except KeyError:
return False # unsigned, not from Anthropic
try:
signed_at = int(ts)
except ValueError:
return False
if abs(time.time() - signed_at) > TOLERANCE_SECONDS:
return False # replayed, or clocks disagree
try:
key = base64.b64decode(secret.removeprefix("whsec_"), validate=True)
except ValueError:
return False # misconfigured secret
payload = f"{msg_id}.{ts}.".encode() + body
expected = b"v1," + base64.b64encode(hmac.new(key, payload, hashlib.sha256).digest())
return any(hmac.compare_digest(expected, s.encode()) for s in sigs.split())
シークレットのローテーション
ローテーションは管理者側では即時のカットオーバーですが、旧シークレットで署名されたリクエストがその後およそ 1 分間到着し続ける可能性があります。加えて、すでに飛んでいるものもあります。そうしたはぐれ者を未署名として拒否しないよう、ローテーション期間中はサーバーが古いシークレットと新しいシークレットの両方の署名を受け入れるようにしてください。
一度きりの例外
組織の初回保存より前 に送られる接続テストは、署名シークレットがまだ存在しないため未署名で届きます。管理者がシークレットの存在を確認するまでは未署名リクエストを受け入れ、その後は拒否してください。
運用上のセマンティクス
タイムアウト
管理者は判定タイムアウトを 1 から 10,000 ms の範囲で設定でき、デフォルトは 5,000 ms です。この予算はラウンドトリップ全体(接続、TLS ハンドシェイク、リクエストボディのアップロード、レスポンスボディのダウンロード)をカバーします。
リトライ
Anthropic は 接続試行が失敗したときにのみ、100 ms の遅延の後、正確に一度だけ リトライします。500 に対しては行いません。タイムアウトに対しても行いません。パースエラーに対しても行いません。サーバーが — 何であれ — 応答した時点で交換は終わりです。リトライは同じタイムアウト予算を共有し、同じ webhook-id と署名を運ぶため、webhook-id で重複排除するのは安全です。
失敗時ハンドリング
判定付きのきれいな 200 以外はすべて Webhook 失敗 です:タイムアウト、200 以外のステータス(リダイレクトを含む)、パース不能または過大なレスポンスボディ、到達不能なエンドポイント。失敗時、組織の設定が決めます:
- リクエストをブロック。 高規制環境向けの安全なデフォルト。DLP サーバーがダウンしていればユーザーはブロックされます。Claude の可用性がスキャナーの可用性になります。
- リクエストを許可。 サーバーが回復する間、ユーザーは作業を続けられます。停止中はプロンプトが未検査で流れます — 多くの組織で受け入れられるトレードオフですが、監査証跡のギャップをどう埋めるか計画してください。
サーキットブレーカー
AI セキュリティサーバーに起因する持続的な Webhook 失敗は、施行を止める サーキットブレーカー をトリップします:Anthropic はサーバー呼び出しをやめ、すべてのリクエストに失敗時ハンドリングが適用されます。回復は自動ではありません — サーバーを直したうえで、管理者が 判定の施行 をあらためてオンに切り替える必要があります。実務的には、これは:未知のトップレベル type は HTTP 500 ではなく {"action": "allow"} を返す べき、ということです — 将来 Anthropic が新しいイベントタイプを出す日に、展開初日でサーキットブレーカー領域に陥りかねません。
レイテンシ
組織内のガバナンス対象リクエストは、それぞれが AI セキュリティサーバーのラウンドトリップぶんのレイテンシを支払います。大きな組織にロールアウトする前に負荷テストを:4 秒のスキャナーはチャットプロンプトでは目に見えなくても、多くのリクエストを連続発火する Claude Code のツールループでは悪夢です。
送信元 IP のアローリスト
リクエストは 160.79.106.0/24 から発信され、これは Anthropic の公開されている 外向き IP レンジ の一部です。このブロックをアローリストに入れてください — 同じページの受信レンジではありません(別リスト)。そしてアローリストは署名検証の代わりにはなりません:このブロックは Inference Hooks 以外の Anthropic 送信トラフィックも運びます。
展開プレイブック
- 最初にオンにするもの。サーバーはすべてのリクエストを評価しますが、deny は施行されません。ユーザーがブロックされる前に、1 週間の実トラフィックに対してルールを調整できます。
- 施行をオンに切り替えるときは、たとえば 10% から始めて上げていきます。スキャナーに誤検知のスパイクが起きたときの爆風半径を切り詰めます。
- スキャナーが追いつけない速度でプロンプトを打ち出すロール(シニアエンジニアリング、オンコール SRE)は、調整中は除外できます。永遠にではありません — ただしランプ期間中は有用です。
- 前の 3 段階が定めた慣らし期間を無事に走った後にだけ。ここでようやく deny_reason 文字列がユーザーに届き始めるので、スイッチを切り替える前にもう一度見直してください。
Anthropic のドキュメントは平易に書いています:初日に従業員をブロックすることこそ、DLP プログラムが死ぬ道である。 シャドウモードには理由があります。
統合を設計する
- webhook-id で重複排除。配信ごとに一意で、ボディの request_id と一致します。接続失敗のリトライはそれを再利用するので、クリーンなべき等性キーになります。
- 各判定を reference_id とともに保存。Anthropic は拒否ごとにアクティビティフィードエントリに reference_id を記録するため、自組織側で拒否を該当スキャン判断へ結合できます。
- 常に許可のアーカイブ統合では、まず応答してから永続化。書き込みを終える前に応答することで、ラウンドトリップをユーザーのクリティカルパスから外せます — ストレージシステムはホットパス上にありません。
- deny_reason は SIEM ではなく人に向けて書く。「プロンプトからクレジットカード番号を削除して再送信してください」は「PCI_REGEX_2A がトリップ、reference 4471」に勝ります。ユーザーは前者に対して行動します。
カバレッジマトリクス
| サーフェス / アクセス | Inference Hooks で検査? |
|---|---|
| claude.ai(Web、デスクトップ、モバイル) | ✅ はい |
| Claude Code(Web、デスクトップ、CLI) | ✅ はい(session_id はベストエフォート、クライアント主張) |
| Claude Cowork | ✅ はい |
| 音声モード | ❌ ベータ対象外 |
| 会話タイトル生成、その他付随処理 | ❌ 送信されません |
| システムプロンプト、ツール定義 | ❌ 送信されません |
| 生のファイル/画像バイト | ❌ 送信されません(抽出テキストは送られます) |
| 画像のみのコンテンツ(例:ドキュメントのスクリーンショット) | ❌ 検査されません |
| Claude Platform API キー(開発者アクセス) | ❌ Inference Hooks の対象外(Enterprise ではなく Platform 組織) |
| Amazon Bedrock / Google Cloud デプロイメント | ❌ これらのプレーンでは利用不可 |
よくある間違い
- HTTP 403 でブロックを示すこと。それは deny ではなく Webhook 失敗です — ポリシー判定は捨てられ、failure handling が代わりに動きます。
- 'allow' または 'deny' 以外の action を返すこと。同じ話:Webhook 失敗。第三の状態を追加したいなら、判定にではなく自組織の監査ログに追加してください。
- 小さすぎるボディ上限のデフォルト(Express 100 kB、nginx 1 MB)。大きな PDF の抽出テキストが含まれた 3 MB のトランスクリプトは、リバースプロキシで 413 になります。10 MB の上限に合わせて限度を上げてください。
- whsec_ シークレットの URL セーフ base64 デコード。すべてのリクエストで無音の定数時間ミスマッチが続き、すべてのリクエストが「未署名」だと気づくまで見えません。
- HMAC 前にボディを再シリアライズ。受け取ったままの生バイトを検証してください。json.loads + json.dumps は空白を変えて署名を壊します。
- 未知のトップレベル `type` を 500 で拒否。Anthropic が新しいイベントタイプを出した日にサーキットブレーカーをトリップします。未知の型には `allow` を返してください。
- 認識しない `source.application` の値を拒否。オープン列挙型です。新しい値が登場するので、既存の統合が壊れてはいけません。
- user/assistant の交互を仮定。ブロックがすべて除外されたターンは `messages` から落とされます。防御的にパースしてください。
Inference Hooks を使うか、クライアント側プロキシを使うか
一部の組織は、従業員のオンライン活動 すべて をカバーするために TLS 傍受プロキシを引き続き運用しています。Inference Hooks はプロキシの置き換えではありません — Anthropic のパリメーターの 内側 に位置する Claude 固有の施行地点であり、線上の暗号化バイトしか見ないプロキシよりもリッチで構造化された会話ビューを見ます。
- モデルが実際に見るもの(ツール呼び出し、添付、トランスクリプト)への構造化アクセス、チャット + Code + Cowork の均一なカバレッジ、端末ごとのインストールなしを求めるなら Inference Hooks を使ってください。
- 箱で行うその他すべて:非 Claude サービスへのファイルアップロード、claude.ai 外のブラウザトラフィック、メール添付には ネットワーク DLP を継続してください。両者は重なりません。
- 事後の監査とエクスポートには Compliance API を追加してください。
クイズ
Check yourself
0/3次へ
- Admin API:Claude 組織を自動化する — Hooks と自然に組み合わさる Enterprise のユーザー管理と RBAC エンドポイント。
- MCP 2026-07-28:ステートレス仕様 — Hooks が
tool_useとtool_resultブロックで見ることになるツール呼び出し側。 - 拒否と安全性 — Hooks がプロンプトを通した 後 に発火する Claude 自身のモデル内拒否シグナル。
- 現在のモデルと価格