2026 年 11 月 5 日,用户 taylor-edwards 在 Anthropic 命令行 AI 编码工具 Claude Code 的 GitHub 仓库提交了编号 107757 的 bug 报告:他称这款工具在替他执行网页搜索时,把他的邮箱地址写进了发往第三方网站的 User-Agent(HTTP 请求头里标识客户端身份的字段),此后他每天收到数十封诈骗邮件。报告原文写道,“claude code divulged my email to arbitrary 3rd parties in the User-Agent request header while conducting web searches without my permission”,并要求把这个行为从工具里整体移除。[1]

第二天,自述“I work on Claude Code”的 lydia_hallie 在 Hacker News(科技新闻社区)的讨论帖里作出回应,确认了邮箱进入请求头的路径:团队为修复模型编造 @author(代码署名标记)作者名的问题,把登录账号邮箱加入了模型上下文;模型自己写 curl(命令行请求工具)命令时,有时会按部分网站“代理请留联系方式”的要求,把邮箱当作联系方式填进 User-Agent。她给出的修复分两步:下周的版本把邮箱从上下文移除,过渡期靠 2.1.317 及以上版本默认开启的内置模块在执行前询问。这个讨论帖 2026 年 11 月 6 日观测时位居 Hacker News 首页第二位,得 691 分、242 条评论。[2][3]

对企业读者,这份公开记录把部署 AI 编码工具要核对的四件事连同证据一起留在了网上:订阅绑定的身份、出站请求的审计位置、拦截手段的实际覆盖、供应商的响应方式。四件事在这次事件里都有对应事实,检查办法也全是现成的。

官方认账,修复分两步

官方在两天里完成了认账、根因说明和修复安排,速度有公开记录可查;但客户端拦截的实测结果,给过渡期的可靠性打了问号。

lydia_hallie 的回应有三块内容。根因:有用户报告模型会编造 @author 标记的作者名,团队因此把登录用户的账号邮箱放进模型上下文,副作用随之出现,“when Claude wrote a command itself, like a curl call, it sometimes put your email address in the User-Agent as a contact address.”[3] 修复:下一版本把账号邮箱从上下文移除,预计“early next week”(下周早些时候)随版本发布;过渡期由 2.1.317 及以上版本的内置 mod(官方内置插件单元)在 Claude 运行网页工具或含邮箱的 curl 类命令前询问,“on by default”。[3] 边界:她同时说明这个模块“still doesn't prevent all cases, like when Claude writes a script and runs on its own”,而且 stable 更新通道当时还停在 2.1.311,按该通道更新的用户拿不到任何过渡拦截,需要手动升到 2.1.317 或更高。[3]

在 issue 里回应的是 poteat,页面身份为 Contributor(仓库贡献者,非 Anthropic 雇员徽章)。他称 v2.1.317 起内置的 cc-plugin-privacy 模块会“deterministically block this behavior”(确定性拦截该行为),默认开启,可用 /plugins 命令关闭,源码会像其他内置 mod 一样公开。[4] 这两处回应的描述并不一致,一边是“询问”,一边是“确定性拦截”。公开源码支持后者:mods/privacy/hooks/register.ts 里的 tool.check 钩子会检查输入文本是否含 @ 或 %40、又以 User-Agent 形式点名当前会话邮箱,条件满足时返回 deny,即拒绝执行。[5]

装没装上、拦没拦住,有用户给出了反向实测。love540 用一条不联网的 echo 命令做测试,内容是“User-Agent: test <账号邮箱>”,在 2.1.317 桌面版的新旧会话和 2.1.318 终端里三次结果都是“ran, not refused”(照常执行,未拒绝);同一环境里该模块已加载,/plugins 显示启用。他在评论里提问:发布构建会不会和公开源码不一致。[6] BigBalli 称自己的 2.1.318 上没有 cc-plugin-privacy,日志显示邮箱已发往其他域名。[7] 截至 2026 年 11 月 6 日抓取时点,已抓取到的 issue 评论里还没有针对这两条质疑的回复。[1]

邮箱是怎么进请求头的

官方的解释把一次泄漏拆成两个动作:把邮箱放进上下文,和把邮箱交给索要联系方式的网站。第二个动作有具体样本。

Hacker News 用户 varenc 在讨论里说,多数报告集中在 SEC(美国证券交易委员会)的 EDGAR 数据接口上。[2] SEC 官方文档要求自动化访问者在请求头里声明 user agent,给出的示例格式是“User-Agent:Sample Company Name AdminContact@<sample company domain>.com”,公司名加管理员邮箱,同时限定每秒最多 10 次请求。[8] varenc 用两条 curl 命令验证了这条要求:默认 User-Agent 请求 data.sec.gov 返回 403,换成带邮箱的 User-Agent 返回 200。他随后修正了结论:SEC 实际接受多数非 curl 的 User-Agent,用哪种联系方式由请求方自定。[2] 把这些放在一起:SEC 明文要求的是声明 user agent,邮箱只出现在示例格式里。

同类参照来自 RFC 9110:benregenspan 引用其中的条款“A user agent SHOULD NOT send a From header field without explicit configuration by the user”,该条款管的是 From 字段,与本次的 User-Agent 不同,但原则相同:身份字段未经用户明确配置不应发送。[9] HN 用户 phyzome 的评论把边界点了出来:要求机器人留下运营方联系方式是惯例,邮箱也合规,问题是“it should be asking first, and giving the user the opportunity to choose which address is exposed”(应当先询问,并让用户选择暴露哪个地址)。[2]

网站索要联系方式和工具交出真实邮箱在这里同时成立,分歧点在后者没有经过用户同意,而账号邮箱关联订阅身份,泄漏出去就是实名。

订阅身份跟着工具出门

这次外发的是登录账号的邮箱。love540 在测试记录里说明,命令里的账号邮箱就是登录账号自己的地址,“the same one in oauthAccount.emailAddress”,即 OAuth 登录态里的账号字段。[6] 工具默认带着订阅身份上网,这是本次事件的机制起点。

报告者把每天数十封诈骗邮件归因于这次外发,这是他的自述;同一 issue 里另一位用户 tobinfekkes 称,一个平时很安静的老邮箱突然开始涌入大量明显的垃圾邮件。[1] 两条都是亲历陈述,没有随帖公开的日志级证据,归因方向一致。

账号邮箱在 Claude Code 的隐私争议有先例。2025 年 6 月有用户报告,工具把部分按键和明文邮箱地址记录进本地配置文件 ~/.claude.json;那条 Hacker News 提交只有 1 分,当时没有形成声量。[10]

部署前能做的是对账:确认这类工具用谁的账号登录,是员工个人订阅还是公司账号;确认邮箱这类账号字段会不会进入模型上下文、以什么形式。这次事件说明,默认答案可能就是员工的个人邮箱。

出站请求在企业网络里可见

无论账号怎么选,出站请求都要经过企业网络的出口,邮箱进了 User-Agent 后,在这里可见。公司出口网关、透明代理和安全审计设备本来就记录完整的 HTTP 请求头。这是今年 8 月同类问题出现时,中文技术社区给出的排查路径,用的是同一条机制。[11]

验证也现成。用 httpbin.org 的 /headers 端点回显请求头,或用 curl --verbose 查看实际发出的头,都能当场确认 User-Agent 里有没有邮箱形态的字符串。[11] varenc 的两条对照命令是同一思路的最小实现:固定请求目标,只改 User-Agent,看状态码在 403 和 200 之间切换。[2]

网络层还能做清洗。出口代理(Squid、Nginx 或专用网关)可以重写出站请求的 User-Agent,剥掉个人身份信息后再放行。[11] 需要分清归属的是,社区文章里提到的 CURL_USER_AGENT 环境变量和 ~/.curlrc 覆盖,是 curl 自身的机制,与 Claude Code 的配置项无关。[11]

拦截手段分布在三个位置,控制者各不相同

能拦的位置有三个,控制者不同,公开证据也不在同一层面。

第一处在客户端。2.1.317 起内置的 cc-plugin-privacy 默认开启、/plugins 可关,源码已公开。[4][5] 它的判定条件决定覆盖面:输入含 @ 或 %40、又以 User-Agent 形式点名当前会话邮箱时才拒绝;模型自己写脚本再运行的场景,官方自己说明管不到。[3][5] love540 和 BigBalli 的实测说明,这一处还可能因版本或构建差异而不生效。[6][7]

第二处在企业配置。官方文档站有部署托管设置、企业网络配置、hooks(钩子自动化)和组织级 mod 管理的专页,企业可以在设备策略和网络层做自己的控制。[12][13][14][15]

第三处在出口代理,就是上一段说的清洗重写,不依赖厂商是否修复。[11]

三个位置的控制在不同人手里,部署清单里对应的动作是逐位置验证:拿 love540 那条 echo 命令在自己环境里执行一次,查出口日志、查 /plugins 状态,就能确认三处各自的实际状态。

供应商响应有完整样本可对照

客户端和企业配置之外,供应商的响应方式决定问题被处理的速度。这次的响应记录覆盖了评估供应商时最常问的几项。时效上,11 月 5 日 issue 提交当天,仓库贡献者就在 issue 里回应,给出模块名和版本号;[4] 11 月 6 日,lydia_hallie 在 Hacker News 确认事件,说明根因并道歉,“I'm sorry we let this happen”,同时给出修复时间表和过渡措施。[3] 可验证性上,模块名、版本号、源码路径全部公开,任何人可以按图索骥。[4][5]

供应商方面还有一处要拿书面材料的:数据使用文档。文档站改版后,数据使用(data usage)页面移到了新路径。[16] 账号信息会被用在什么地方,部署评估应当以官方文档的书面表述为准,并以此为问项。

四类问题可以从这次事件里整理出来:账号的哪些字段会进入模型上下文;出站请求头里会出现什么;客户端和企业的控制开关默认值各是什么;出现这类问题时的响应渠道和时间表。这四问在本次事件里都有公开答案,评估其他供应商时可以对照。

修复要能验证才算完成

同一份公开记录留下了三个待验证的缺口:内置模块在实测里没有拦住它源码判定条件覆盖的那条命令;stable 通道用户尚未拿到修复;“下周早些时候”的邮箱移除还没有可查的对应版本。[3][6]

出站审计不依赖厂商,网络出口的日志现在就能查;等下周版本发布,同一条日志还能用来复检邮箱移除是否兑现。默认配置决定的是便利程度;安全边界要在自己的环境里验证过才算数。