Claude Codeがメールアドレスを外部へ送る問題:2.1.317の対策で止まらない経路と、ログが消える前の確認方法

当ページのリンクには広告が含まれています。

「Web検索を頼んだだけなのに、自分のメールアドレスが第三者のサイトに送られ、毎日数十通の詐欺メールが届くようになった」。日本時間11月6日の朝、Claude Codeの利用者がこうGitHubに報告しました。Hacker Newsでは700点を超える反応があり、Anthropicの開発者は「次のリリースで、モデルに渡すメールアドレスを外す」と答えています。

では、今すぐ最新版に更新すれば止まるのか。現時点の答えは「いいえ」です。2.1.317から既定で入った対策(cc-plugin-privacy)が止めるのは、シェルのコマンドにUser-Agentとメールアドレスを一緒に書く形だけです。WebFetch、MCPツール、Claudeが書いたスクリプト経由の送信は対象外だと、公開されたソースの説明に明記されています。しかも私の環境(macOS、2.1.318)では、その1つの形すら止まりませんでした。

次のリリースが出るまでにできることは3つあります。最新版にしておくこと、会話ログが30日で消える前に送信の有無を確かめること、外部のサイトやAPIを自動で何度も呼ぶ作業ではUser-Agentを自分で決めて指示することです。以下では、その根拠と、ログを読み取るだけの確認スクリプトを示します。

目次

Claudeは「行儀のよいクライアント」として連絡先を書いていた

Hacker NewsでClaude Codeチームのlydia_hallie氏が説明した経緯はこうです。コードの@author欄にClaudeが架空の名前を書くという報告があり、ログインして使う利用者については、アカウントのメールアドレスをモデルに渡す文脈へ入れた。ところがClaudeがcurlなどのコマンドを自分で書くとき、そのアドレスをUser-Agentに連絡先として入れることがあった、というものです。

背景には、自動で接続するプログラムに連絡先の明記を求めるサイトの存在があります。Anthropicの別の開発者は、Wikipediaの調査を頼まれたClaudeが、Wikipediaの方針に従ってcurlにユーザーのメールを入れる流れを例に挙げました。誰かが「メールを送る機能」を作ったわけではありません。モデルが行儀よく振る舞おうとした結果、手元にあった本人のアドレスを使ってしまったわけです。

この問題は新しくありません。同じ指摘のissueは7月から開いたままです。変更履歴によると、8月18日(日本時間)公開の2.1.234で「アカウントのメールは本人の識別にだけ使い、頼まれない限り無関係なサービスへ送らない」という指示がClaudeに追加されました。それでも今回の報告が出ています。8月には、Autoモードでデータ収集を任せたら約4000回送られていたという体験記もQiitaに投稿されていました。モデルへの指示だけでは止めきれなかった、というのがここまでの経緯です。

対象は、claude.aiのアカウントでログインしてClaude Codeを使う人です。会社のアドレスでログインしていれば、送られるのは会社のメールです。issueには、User-Agentにメールだけでなく、作業中のプロジェクト名らしき名前も入っていたという報告もあります。どの会社の誰が何を調べているかが、一つのヘッダーで相手に伝わる可能性があります。

2.1.317の対策modが止めるのは、シェルでUser-Agentを書く形だけ

Anthropicは暫定策として、組込みのmod(Claude Codeに同梱される拡張機能)cc-plugin-privacyを2.1.317以降で既定で有効にしました。11月6日にソースと説明が公開されています。読むと、対象はかなり狭いことが分かります。

Claudeの動きmodの扱い
Bash・PowerShellのコマンドに、アカウントのメールとUser-Agentの指定(curl -A、wget -U、User-Agent:、UA=など)が両方ある拒否する。確認画面は出ない
WebFetch・WebSearchでの取得調べない
MCPツール(ブラウザー操作など)調べない
Claudeが書いたスクリプトを実行する(アドレスはファイルの中)調べない
$(git config user.email)のように実行時に取り出す調べない
URLや送信本文、From:ヘッダーに入れる調べない

説明文自身が「これは礼儀であって境界ではなく、1つの形だけを扱う」と書いています。報告の多くは「Claudeが書いたcurl」なので、そこに絞った応急処置だと読めます。issueには、8月から11月までのログで53回送っていて、大半はサブエージェントが書いたcurl -Aだったという報告がありました。同じ処理をスクリプトに書き出してから実行していれば、modの対象外です。

注意したいのは、変更履歴の書き方です。2.1.317の項目には「Webツールやシェルツールがアカウントのメールをネットワークに送る前に確認する」とあり、日本語の解説記事にもその前提で書かれたものがあります。ところが公開された説明では、WebFetchとWebSearchは対象外で、動作は確認ではなく無条件の拒否です。「Webツールも確認してくれる」と読んで安心しないでください。

もう一つの落とし穴が、更新チャンネルです。lydia_hallie氏によると、11月6日時点でstableチャンネルはまだ2.1.311で、このmodが入っていません。Homebrewのclaude-codeはstableを追うので、brew upgradeしても入らない可能性があります。/pluginの一覧にcc-plugin-privacyがあるかどうかで確かめられます。ついでに、Postmanの件のように、自分で入れた覚えのないプラグインが並んでいないかも見ておくとよいでしょう。

2.1.318で試すと、modは読み込まれていたのに止まらなかった

表で「拒否する」とした形が実際に止まるかを、私のMac(macOS、Claude Code 2.1.318、claude.aiアカウントでログイン)で試しました。使ったのは、ネットワークに何も送らないコマンドです。

echo 'User-Agent: test <アカウントのメールアドレス>' > /dev/null

claude -pで新しいセッションを起動し、起動時の情報にcc-plugin-privacy@builtinが読み込まれていることを確かめてから、このコマンドを実行させました。結果は「Bash completed with no output」で、エラーも確認画面もなく実行されました。対話中のセッションで同じコマンドを打っても同じでした。ソースの判定条件(入力に@、アカウントのアドレス、User-Agentの語)はすべて満たしています。

同じ結果はissueにも報告されています。macOSのデスクトップ版2.1.317とターミナル版2.1.318でechoが通ったという報告と、Windows 11の2.1.318ではcurl -Aも止まらず、存在しないドメインへの接続を試みたという報告です。私はcurl -Aの形は試していません。原因は分かりません。説明文には、サーバー側のスイッチやフックの仕組みが読み込まれないセッションでは働かない、とあります。少なくとも、「一覧に表示されているから守られている」とは言えない状態です。

送ったかどうかは、消える前の会話ログで分かる

過去に送ってしまったかは、手元の会話ログで確かめられます。Claude Codeは会話を~/.claude/projects/に平文で保存しており、公式の説明では既定の保存期間は30日です。それより古いログは消えるため、調べるなら早いほうがよい、ということになります。

次のスクリプトは、ログに残るツール呼び出しのうち、入力にアカウントのメールアドレスを含むものを一覧にします。読み取るだけで、どこにも送信しません。表示するのは日付・ツール名・宛先のホスト・User-Agent指定の有無だけで、アドレスそのものやコマンドの全文は出しません。Python 3の標準ライブラリだけで動きます。

"""Claude Codeの会話ログから、メールアドレスを含むツール呼び出しを一覧にする(読み取りのみ・送信なし)。"""
import json
import re
import sys
from pathlib import Path
from urllib.parse import quote

HOME = Path.home()
URL_HOST = re.compile(r"https?://([A-Za-z0-9.-]+)")
UA_WORDS = re.compile(r"user[-_ ]?agent|\bua\s*[=:]|\s-A\s|\s-U\s", re.IGNORECASE)


def addresses():
    found = [a.lower() for a in sys.argv[1:]]
    config = HOME / ".claude.json"
    if config.exists():
        account = json.loads(config.read_text()).get("oauthAccount") or {}
        if account.get("emailAddress"):
            found.append(account["emailAddress"].lower())
    return {form for a in found for form in (a, quote(a).lower())}


def tool_calls(path):
    for line in path.open(errors="replace"):
        try:
            entry = json.loads(line)
        except json.JSONDecodeError:
            continue
        content = (entry.get("message") or {}).get("content")
        if not isinstance(content, list):
            continue
        for item in content:
            if isinstance(item, dict) and item.get("type") == "tool_use":
                yield entry.get("timestamp", "")[:10], item.get("name", "?"), json.dumps(item.get("input"))


def main():
    needles = addresses()
    if not needles:
        print("ログイン中のメールアドレスが見つかりません。調べるアドレスを引数で指定してください")
        return
    files = sorted((HOME / ".claude" / "projects").rglob("*.jsonl"))
    hits = []
    for path in files:
        for day, tool, text in tool_calls(path):
            if any(n in text.lower() for n in needles):
                hosts = ",".join(sorted(set(URL_HOST.findall(text)))) or "URLなし"
                hits.append((day, tool, hosts, "UA指定あり" if UA_WORDS.search(text) else "-"))
    print(f"ログ{len(files)}件を確認。メールアドレスを含むツール呼び出し: {len(hits)}件")
    for hit in sorted(hits):
        print("  " + " | ".join(hit))


if __name__ == "__main__":
    main()

check_claude_email.pyという名前で保存し、ターミナルでpython3 check_claude_email.pyと実行します。gitに別のアドレスを設定している場合は、python3 check_claude_email.py 別のアドレスのように引数で追加できます。このとき、Claude Codeに実行を頼まず、自分のターミナルで動かしてください。頼むと、そのコマンド自体がアドレス入りで会話とログに残ります。

私のMacで実行した結果です(10月6日から11月7日までのログ。日付は協定世界時)。

ログ159件を確認。メールアドレスを含むツール呼び出し: 5件
  2026-11-03 | mcp__claude-in-chrome__browser_batch | ai-work-lab.com | -
  2026-11-06 | Bash | URLなし | UA指定あり
  2026-11-06 | Bash | URLなし | UA指定あり
  2026-11-06 | Bash | URLなし | UA指定あり
  2026-11-06 | Bash | URLなし | UA指定あり

11月6日の4件は、上の試験で私が打ったコマンドです。残る1件は、自サイトの問い合わせフォームが動くかをClaudeにブラウザーで試してもらったとき、メール欄に入力したものでした。自分で頼んだ作業ですが、MCPのブラウザー操作なので、modは調べない経路です。第三者のサイトへUser-Agentで送った記録は、残っている30日分には見つかりませんでした。

結果の読み方は次のとおりです。外部のホスト名と「UA指定あり」が並ぶ行は、そのサイトへ送った可能性が高い記録です。「URLなし」で「UA指定あり」の行は、ClaudeがUser-Agent入りのスクリプトやコマンドを書いた記録なので、その日の作業でどこへ接続したかを確かめます。WebFetchにホスト名がある行は、URLそのものにアドレスが入っていました。

このスクリプトで分からないこともあります。実行時にファイルやgit configからアドレスを読むスクリプトは、ログにアドレスが残りません。30日より前の分も見えません。CLAUDE_CONFIG_DIRで保存場所を変えている場合と、Windowsでの動作は確かめていません。0件でも「一度も送っていない」とは言い切れず、「残っているログには記録がない」という意味です。

外部のホスト名が出てきた場合、送ったアドレスは取り戻せません。迷惑メールのフィルターを見直し、会社のアドレスなら、出てきたホスト名と日付を添えて社内の担当者に伝えてください。

次のリリースまでに、自分で決めておくこと

Anthropicは、メールを文脈から外す次のリリースは来週初めの予定だとしています。それまでの数日は、次の順で手を打つのが現実的だと考えます。

  1. claude --versionで2.1.317以上かを見る。下ならclaude updateで更新する。効果は限定的でも、Anthropicが勧める最低限の状態です。
  2. 上のスクリプトでログを確かめる。外部のホスト名が出たら、その日の作業を思い出して送り先を特定する。
  3. APIからのデータ収集やスクレイピングを頼むときは、「User-Agentはmy-research/1.0とし、個人のメールアドレスやプロジェクト名を入れない」のように、値を自分で指定する。Autoモードなど確認なしで任せる場合は、実行前にClaudeが書いたスクリプトのヘッダー部分だけでも読む。

3つ目はモデルへの指示と人の目視なので、確実ではありません。2.1.234の指示で止まらなかった以上、これだけで足りるとは考えていません。仕組みで止めたい場合は、ツールを実行する前にアドレスを含む入力を拒否するフックを自分で入れる方法があります。WebFetchやMCPまで対象にしたZennの実装例が参考になります。私はこの実装を試していません。

Claude Codeの使用をやめるほどの問題だとは考えていません。ただ、「更新したから大丈夫」とは、今の時点では言えません。指示を足しても、modを足しても、モデルの手元にアドレスがある限り、どこかの経路から出ていく余地が残ります。次のリリースでアドレスがモデルに渡されなくなったかを確かめたら、この記事に追記します。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

IT業界で10年、ソフトウェア開発をしてきたエンジニアです。今もChatGPT、Codex、Claude Codeを中心に、開発の効率化や生産性向上にAIをフル活用しています。応用情報技術者。登壇や執筆もしています。このサイトでは、仕事でAIを使う立場から、料金や利用条件、使い方を調べてまとめています。

目次