Claude Code 邮箱外泄报告与自查线索
文章称 Claude Code 曾通过网页请求的 User-Agent 暴露账户邮箱,并指出系统提示词中也存在邮箱注入问题。作者称新版已提供隐私修复,并介绍用于检查本地安装行为的五项检查。
AI 深度解读
Claude Code 被指通过两条路径携带用户邮箱:联网工具把它放进 User-Agent,每次会话又把它写进系统提示词。前者可能直接暴露给第三方网站,后者则把登录资料变成模型上下文。原文称,官方已提供默认启用的隐私模块,但报告者使用更新版本仍遇到问题。
这件事值得程序员关注,因为版本号不能代替运行验证。文章最有用的是检查思路;至于垃圾邮件的来源、修复覆盖范围,以及作者对响应速度的批评,还需要分开判断。
邮箱为什么会跟着 Agent 出门
第一条路径是 HTTP 请求头里的 User-Agent。它通常用来说明客户端类型和版本,网站服务端也经常记录这个字段。按原文转述,Claude Code 的联网工具把登录账户邮箱放进了这里,于是 Agent 访问第三方网站时,对方可能同时拿到用户身份。
网站需要识别自动化流量,并不需要知道账户邮箱。产品名、版本和自动化客户端标记已经可以承担分类作用;加入邮箱,却把一次工具请求和具体的人关联起来。
对前端、全栈开发者来说,这类似于把用户资料拼进每个请求的诊断字段。即使业务接口没有主动保存,反向代理、网关或应用日志仍可能留下副本。不过,原文把它形容为沿途所有中间方都能收到一次表单,表达过重。能否看到请求头,还取决于 HTTPS 在哪里终止、谁能访问解密后的请求和日志。
报告者称,自己每天收到数十封诈骗邮件。这是需要调查的现象,但原文没有提供从某次请求、某份日志到具体邮件的追踪证据,不能直接认定这些邮件全部由该泄漏造成。
请求头携带邮箱和垃圾邮件来源,是两个需要分别举证的问题。
系统提示词里的邮箱,又多了一种风险
第二条路径来自 GitHub issue #81138。按原文记录,该问题提交于 2026-07-25,引用的是 v2.1.220 发布包中的代码。
下面这段代码说明,邮箱怎样被组织成提供给模型的文字:
let n = process.env.ANTHROPIC_UNIX_SOCKET ? void 0 : dc()?.emailAddress
return {
...(n && { userEmail: `The user's email address is ${n}.` }),
...
}
要看第一行:设置了 ANTHROPIC_UNIX_SOCKET 时,这段逻辑不会读取邮箱。因此,原文反复使用的「每次会话」也有条件,不能脱离代码直接理解成所有运行方式。
原文称,数据来自 ~/.claude.json 中的 oauthAccount.emailAddress,随后形成带有 # userEmail 标记的系统提示词。模型因此可能在用户没有主动提供邮箱时,就已经知道它。
这与 User-Agent 的接收方不同。请求头里的邮箱可能被目标网站直接看到;系统提示词里的邮箱则先进入模型服务的处理链路,之后是否进入日志、工具参数或输出内容,还取决于具体实现和会话行为。原文没有逐项展示这些后续传播路径。
问题仍然成立:为了登录提供的资料,为什么需要成为任务上下文?如果任务只是改组件或排查 Node 服务,账户邮箱通常不是完成工作的必要输入。
默认开启隐私模块,为什么还要自己检查
原文称,11 月 5 日 22:13 提交的 issue #107757 在一天内获得数百个回应,文章记录时已有 360 个 reactions。同日,一位 Anthropic 贡献者回复:v2.1.317+ 内置 cc-plugin-privacy,默认开启,可通过 /plugins 切换,并称它会确定性地阻止该行为,源码将随其他内置模块公开。
但报告者使用的是 v2.1.318,仍声称观察到了泄漏。原文提出模块被关闭、拦截存在缺口、版本信息混淆等可能性,没有给出最终结论。
因此,五项检查里最有价值的是观察实际请求。下面的监听器会明确打印收到的 User-Agent:
python3 -c "from http.server import *; import sys
class H(BaseHTTPRequestHandler):
def do_GET(self):
print('UA =', self.headers.get('User-Agent'), file=sys.stderr, flush=True)
self.send_response(200); self.end_headers(); self.wfile.write(b'ok')
HTTPServer(('127.0.0.1', 8899), H).serve_forever()"
要看读取 self.headers.get('User-Agent') 的那一行,它输出的是监听器实际收到的字段。启动后,让 Claude Code 获取 http://127.0.0.1:8899/probe,再检查结果。
原文先给了 mkdir /tmp/uatest && cd /tmp/uatest && python3 -m http.server 8899 &,随后又启动上述监听器。照顺序执行可能发生 8899 端口占用;默认 http.server 的访问日志也不会直接打印 User-Agent。只运行明确打印请求头的监听器,更容易得到有效结果。
这个测试还有一个前提:请求确实通过被怀疑的联网工具发出。如果 Agent 改用 shell 中的其他客户端,测到的是另一条路径;如果工具拒绝访问回环地址,也不能据此判定已经修复。
其余检查可以帮助补齐上下文:用 claude --version 看版本,在 /plugins 确认模块状态,检查本地配置是否存在邮箱字段,再开新会话询问模型是否知道邮箱。作者自己的机器没有安装 CLI,配置里也没有 oauthAccount,所以这套流程并不是作者在受影响安装上的完整复现记录。
我的判断
作者对修复方向的判断合理:这类问题适合在代码中阻止敏感字段进入出站请求,不能只靠提示词要求模型保密。但原文没有提供模块实现或覆盖测试,无法据此确认两条泄漏路径都已解决。
时间线也需要纠正。2026-07-25 到 11 月 5 日约三个半月,与标题和正文所说的「五个月」不一致。新问题受到关注后同日出现修复回复,可以支持追问响应流程,却不能单独证明修复完全由热度推动。文中「按 emoji 数排序、用 80/20 处理队列」的评论是讽刺,不是维护流程的证据。
另外,询问「我的邮箱是什么」只能作为线索。回答正确,还要排除工作区文件和其他上下文来源;回答不知道,也不能证明系统提示词没有邮箱。原文把这一步称为直接确认,结论过强。
一次请求没有携带邮箱,只能证明这次被测路径的结果。
能拿来做什么
- 检查常用 Claude Code 环境的版本和
cc-plugin-privacy状态,并记录实际发起请求的工具,避免把其他客户端的结果当成联网工具的验证结果。 - 在有权限的测试服务上观察请求头,先确认请求到达,再检查 User-Agent 是否含邮箱;日志分享前应遮盖个人信息。
- 开发 Node 工具或 Agent 联网适配层时,使用产品名、版本等固定信息构造 User-Agent,避免把登录账户资料直接拼入请求头。
- 把账户认证资料与模型任务上下文分开处理;需要邮箱的任务再明确传入,并检查最终进入模型请求和工具参数的字段。