CLAUDE LABEN
FORK — Claude Code 2.1.212で/forkの挙動が変わりました。会話を新しいバックグラウンドセッションへ複製し、作業を続けたまま並走できます。従来のセッション内サブエージェントは/subtaskに移りましたLIMITS — WebSearchの呼び出しがセッション単位で既定200回に制限されました。サブエージェントの起動も既定200回が上限で、暴走した検索・委譲のループを止められますMCPBG — 2分を超えるMCPツール呼び出しは自動的にバックグラウンドへ移り、セッションが固まらなくなりました。しきい値はCLAUDE_CODE_MCP_AUTO_BACKGROUND_MSで調整できますPLANFIX — プランモードがtouchやrmといったファイルを変更するBashコマンドを、許可プロンプトもcanUseToolコールバックも通さずに実行してしまう不具合が修正されましたSONNET5 — Claude Sonnet 5は導入価格として入力100万トークンあたり2ドル、出力10ドルで提供中です。8月31日を過ぎると3ドルと15ドルに戻りますIPO — Anthropicが早ければ10月の株式公開を視野に、引受銀行が投資家との面談を組み始めたと報じられていますFORK — Claude Code 2.1.212で/forkの挙動が変わりました。会話を新しいバックグラウンドセッションへ複製し、作業を続けたまま並走できます。従来のセッション内サブエージェントは/subtaskに移りましたLIMITS — WebSearchの呼び出しがセッション単位で既定200回に制限されました。サブエージェントの起動も既定200回が上限で、暴走した検索・委譲のループを止められますMCPBG — 2分を超えるMCPツール呼び出しは自動的にバックグラウンドへ移り、セッションが固まらなくなりました。しきい値はCLAUDE_CODE_MCP_AUTO_BACKGROUND_MSで調整できますPLANFIX — プランモードがtouchやrmといったファイルを変更するBashコマンドを、許可プロンプトもcanUseToolコールバックも通さずに実行してしまう不具合が修正されましたSONNET5 — Claude Sonnet 5は導入価格として入力100万トークンあたり2ドル、出力10ドルで提供中です。8月31日を過ぎると3ドルと15ドルに戻りますIPO — Anthropicが早ければ10月の株式公開を視野に、引受銀行が投資家との面談を組み始めたと報じられています
記事一覧/API & SDK
API & SDK/2026-05-10中級

Claude API の metadata.user_id は何のために送るのか — abuse 検知とプライバシーの折り合いを設計する

Claude API の metadata.user_id は abuse 検知の精度を上げるためのフィールドですが、生のメールアドレスをそのまま渡すとプライバシー上の問題が出ます。HMAC で安定した擬似 ID を作る具体的な実装と、渡すべき/渡さない場面の判断基準をまとめました。

claude-api81metadataabuse-detectionprivacy2rate-limit7production87

個人運営しているチャットアプリで、初めて Anthropic から「特定のリクエストパターンが疑わしいので確認したい」という連絡を受けたとき、私はとっさに「どのユーザーですか?」と返事ができませんでした。当時、リクエストには metadata.user_id を一切渡しておらず、サーバー側のログとも紐付けられない状態だったからです。

metadata.user_id は Messages API のリクエストにオプションで渡せる小さなフィールドで、ドキュメントには「ユーザーごとの ID を渡せる」とだけ書かれています。けれど、このフィールドが実際に何のために使われ、何を渡すと何が変わるのかを正面から説明している資料はあまり多くありません。私自身がアプリ運用で踏んだ落とし穴と、現在採用している実装パターンを共有します。

metadata.user_id は「abuse 検知のためのヒント」として設計されている

Anthropic の利用規約と API リファレンスを読み合わせると、metadata.user_id の役割は明確です。Anthropic 側が異常なリクエストパターンを検知したとき、それが「あなたのサービス全体の問題」なのか「特定の一人のユーザーの問題」なのかを区別するためのシグナルとして機能します。

このフィールドがないと、Anthropic から見えるのはあなたの API キーだけです。一人のユーザーが大量の不適切なプロンプトを投げた場合、その挙動はサービス全体の挙動として記録され、最悪の場合あなたの API キー全体に対して制限がかかる可能性があります。user_id を渡しておくと、Anthropic は「この user_id のリクエストだけが疑わしい」と判定でき、対応の粒度が細かくなります。

つまり、これは「あなたのアカウント全体を守るためのフィールド」と捉えるのが正しい理解です。ユーザートラッキングや分析のためのものではありません。

何を渡すと何が変わるか — 私の運用での実測

私の小規模なチャットアプリ(月間数千リクエスト)で、user_id あり/なしの状態を1ヶ月ずつ運用して比較したところ、Anthropic から発生した通知の文面に明確な違いが出ました。

user_id なしの期間に問題が起きたとき、メール本文には「貴サービス全体で〜」という抽象的な記述しかなく、私の側では全リクエストログを順に読み返す必要がありました。一方、user_id ありの期間では、通知メールに user_id: <ハッシュ値> が記載されており、自分のサービス側 DB との照合が一発で済みました。

さらに副次的な効果として、Anthropic 側の per-user レートリミット判定が user_id 単位で行われるようになるため、一人のユーザーがバーストしたときに他のユーザーが巻き添えで 429 を受け取る確率が下がります。これは小さなアプリでは見えにくい違いですが、ユーザー数が数百人を超えてくると体感できるようになりました。

生のメールアドレスや UUID をそのまま渡してはいけない

ここが最大の落とし穴です。私自身、最初の実装では metadata.user_id にユーザーのメールアドレスをそのまま入れていました。これは2つの意味で問題があります。

ひとつは、Anthropic のサーバーに自社ユーザーの個人情報が転送されてしまうこと。プライバシーポリシーや利用規約上、本人同意の範囲を超えてしまう可能性があります。もうひとつは、Anthropic 側のログに残った user_id が「メールアドレスそのもの」として読める状態になっていること。万が一の漏洩リスクに対しても明らかに脆弱です。

公式の推奨は「PII を含まない、自社内で安定した識別子」を渡すことです。私の現在の実装は、内部 user_id をサーバー側のシークレット(環境変数で管理)と組み合わせて HMAC-SHA256 でハッシュ化し、その先頭 16 文字を渡す形に統一しています。

# anthropic_user_id.py
import hashlib
import hmac
import os
 
# 環境変数で管理する固定ソルト(ローテーションは慎重に)
USER_ID_SALT = os.environ["ANTHROPIC_USER_ID_SALT"]
 
def to_anthropic_user_id(internal_user_id: str) -> str:
    """
    内部ユーザー ID を HMAC で安定した擬似 ID に変換する。
    同じ internal_user_id からは常に同じ値が返るため、
    Anthropic 側でユーザー単位の追跡が可能になる。
    """
    digest = hmac.new(
        USER_ID_SALT.encode("utf-8"),
        internal_user_id.encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()
    return digest[:16]  # 先頭 16 文字で十分な一意性
 
# 期待出力: 同じ入力に対して常に同じ 16 文字が返る
# >>> to_anthropic_user_id("user_42")
# '8a3f1c9b2e4d6f01'

ポイントは「ハッシュ化後の値が同じユーザーなら常に同じである」こと。Anthropic 側で「同一ユーザーからの連続リクエスト」と判定してもらうために、リクエストごとに変わるランダム値ではなく、内部 ID から決定論的に導出する必要があります。

ソルトを後から変更すると過去の user_id との連続性が切れてしまうため、運用開始前に決めて、原則として変更しないことを前提に置いてください。漏洩時の対応として変更するのは構いませんが、その場合は Anthropic 側の履歴とのリンクは切れる前提で運用判断します。

metadata を渡さない方がよい場面もある

すべてのケースで user_id を送るべきかというと、そうでもありません。私の中では次の場面では渡さない判断をしています。

  • CLI ツールやローカル開発支援ツール: 「1 プロセス = 1 ユーザー」になっているので、API キー単位の判定で十分
  • 社内向けの管理ツール: 利用者が限定されており、Anthropic 側で per-user に分離する意味が薄い
  • 匿名性が重要なユースケース: 例えば相談系のサービスなど、サーバーログにすら user_id を残さない方針のもの

逆に、不特定多数のエンドユーザーを抱える Web サービスでは、ほぼ常に渡しておく方が安全側に倒れます。サービスの性質に応じて、user_id を「abuse 防御の保険として渡す」か「プライバシー方針に沿って意図的に渡さない」かを最初に決めておくと、後から方針がぶれません。

Messages API での実装

実際にリクエストに乗せる箇所はシンプルです。SDK の場合、metadata パラメータに dict を渡すだけで完結します。

import anthropic
from anthropic_user_id import to_anthropic_user_id
 
client = anthropic.Anthropic()
 
def chat(internal_user_id: str, prompt: str) -> str:
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        metadata={
            # ハッシュ化済みの安定 ID を渡す
            "user_id": to_anthropic_user_id(internal_user_id),
        },
        messages=[
            {"role": "user", "content": prompt},
        ],
    )
    return response.content[0].text
 
# 使用例
# >>> chat("user_42", "今日の天気を教えてください")
# 'お住まいの地域を教えていただければ...'

エラーハンドリングや stop_reason の扱いは Claude API の stop_reason を読み解く で詳しく書いていますので、合わせて参照してください。コスト最適化を併用したい場合は Claude API のプロンプトキャッシュがヒットしない時の診断手順 も役に立つはずです。

Web API 全般の安全設計を整理する一冊として手元に置いています。

ソルトのローテーションは慎重に

ハッシュ化を最初に導入したとき、私は ANTHROPIC_USER_ID_SALT を他のトークンと同じローテーション周期に乗せてしまい、四半期ごとに全ユーザーのハッシュ値が一斉に変わる状態を作ってしまいました。Anthropic 側から見ると、私のサービスのユーザーが定期的に「全員入れ替わる」ように見えてしまい、せっかく安定 ID を渡している意味がほぼ失われていました。

今は ANTHROPIC_USER_ID_SALT を「通常のシークレットローテーションには含めない長寿命の値」として明示的に Runbook に書いています。万が一の漏洩で本当にローテーションが必要になった場合は、Anthropic 側に蓄積されていた user_id ベースの履歴は途切れる前提で、一度きりのイベントとして調整します。短期的な連続性を犠牲にしてでも漏洩経路を断つ判断ですが、事前に決めておくと当日の運用判断が楽になります。

もう一点、実務上気をつけているのは「ハッシュ化後の値を自社ログにも残す」ことです。ハッシュ計算を都度メモリ上で行うだけにしてしまうと、数ヶ月後に「このハッシュは誰のものか」を追えなくなる場面があります。リクエストごとに「内部 ID とハッシュ値のペア」を1行ログに残しておくと、後から問い合わせが来てもすぐに紐付けられます。

次に試してほしいこと

まずは自分のサービスのユーザー識別子を1つ選び、上のサンプルにある to_anthropic_user_id を組み込んで、Messages API のリクエストに metadata.user_id を載せてみてください。サーバー側のログには「内部 ID とハッシュ化後の値の対応表」を残しておくと、Anthropic からの問い合わせがあったときにすぐに突合できます。

この一手間だけで、サービス全体の防御力が一段上がります。お読みいただきありがとうございました。

シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

API & SDK2026-07-13
Claude API の同時実行を束ねる — シングルフライトで重複推論とキャッシュ・スタンピードを防ぐ
同一プロンプトが同時に何度も Claude へ飛ぶ重複推論を、シングルフライト(request coalescing)で束ねる設計です。プロセス内・分散環境の実装、ジッター付きリトライ、負のキャッシュまで実測付きでまとめました。
API & SDK2026-06-16
Claude API の PII マスキングは台帳運用で決まる — 復元・暗号化・漏洩率の本番設計メモ
Claude API へ送る前の PII マスキングで本当に難しいのは検出ではなく、復元用トークン台帳の運用です。暗号化保存・多インスタンス共有・漏洩率の継続測定までを動くコード付きで整理します。
API & SDK2026-04-30
Claude APIをCloudflare Queuesで非同期処理する:障害復旧と優先度制御を含めた本番設計
Claude APIをCloudflare Queuesで非同期処理する本番グレードのパイプライン設計を、Producer/Consumer両方の動作コード・障害時のリトライ戦略・優先度制御・コスト制御まで含めて解説します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →