<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>逆向分析 on KnightLi的博客</title>
        <link>https://knightli.com/tags/%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/</link>
        <description>Recent content in 逆向分析 on KnightLi的博客</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <lastBuildDate>Wed, 01 Jul 2026 21:15:26 +0800</lastBuildDate><atom:link href="https://knightli.com/tags/%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Claude Code 被曝在请求里夹暗号：一个撇号就能标记你的环境</title>
        <link>https://knightli.com/2026/07/01/claude-code-prompt-steganography-detection/</link>
        <pubDate>Wed, 01 Jul 2026 21:15:26 +0800</pubDate>
        
        <guid>https://knightli.com/2026/07/01/claude-code-prompt-steganography-detection/</guid>
        <description>&lt;p&gt;最近围绕 Claude Code 有一轮争议：有逆向分析者称，某些版本的 Claude Code 会在特定条件下读取本机环境信息，并把检测结果编码进发往服务端的 system prompt 里。这个机制不是额外上传一个明显的 telemetry 字段，而是修改一行看起来很普通的日期提示，例如 &lt;code&gt;Today&#39;s date is 2026-06-30.&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;公开讨论里最值得看的，不是“封号”这个结果，而是检测流程本身。它展示了一个本地 AI 编程工具如何在不增加额外网络请求的情况下，把客户端环境特征夹带进正常请求。对开发者来说，这比单纯的账号风险更值得警惕。&lt;/p&gt;
&lt;p&gt;本文只整理目前公开逆向文章和社区讨论中反复出现的技术细节。由于这类分析依赖具体版本和逆向结果，下面内容应理解为“公开报告所称的实现逻辑”，不等同于官方完整说明。&lt;/p&gt;
&lt;h2 id=&#34;先把流程说白&#34;&gt;先把流程说白
&lt;/h2&gt;&lt;p&gt;这套逻辑如果用一句话概括，就是：先看你有没有走自定义 API 地址，再看这个地址像不像特定名单里的服务，再看你电脑时区，最后把判断结果藏进一句日期提示里。&lt;/p&gt;
&lt;p&gt;按公开分析里的说法，链路大概是这样：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Claude Code 启动或发请求前，读取 &lt;code&gt;ANTHROPIC_BASE_URL&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;如果这个变量不存在，或者仍然指向官方默认地址，后面的特殊标记逻辑可能不会触发。&lt;/li&gt;
&lt;li&gt;如果它指向自定义 endpoint，就解析 URL，取出 hostname。&lt;/li&gt;
&lt;li&gt;客户端把 hostname 拿去和内置名单、关键词规则做匹配。&lt;/li&gt;
&lt;li&gt;同时读取本机时区，判断是否是 &lt;code&gt;Asia/Shanghai&lt;/code&gt; 或 &lt;code&gt;Asia/Urumqi&lt;/code&gt; 这类目标时区。&lt;/li&gt;
&lt;li&gt;客户端不新增单独字段，而是修改 system prompt 里的日期句子。&lt;/li&gt;
&lt;li&gt;服务端收到请求后，通过日期分隔符和撇号码点，读出客户端环境标记。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这个过程最绕的地方在第 6 步。它不是写一个很明显的字段，比如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;region&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;CN&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;proxyMatched&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;而是把信息塞进这种看起来很正常的文本里：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Today&amp;#39;s date is 2026-06-30.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;普通人扫一眼只会觉得这是日期。真正传递信息的，是日期里的 &lt;code&gt;/&lt;/code&gt; 或 &lt;code&gt;-&lt;/code&gt;，以及 &lt;code&gt;Today&#39;s&lt;/code&gt; 中间那个看起来几乎一样的撇号。&lt;/p&gt;
&lt;p&gt;如果写成伪代码，逻辑大概像这样：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;baseUrl = readEnv(&amp;#34;ANTHROPIC_BASE_URL&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;if baseUrl is empty or baseUrl is official api.anthropic.com:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    send normal system prompt
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;else:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    hostname = parseHostname(baseUrl)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    domainHit = matchDomainList(hostname)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    labHit = matchAiLabKeywords(hostname)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    timezoneHit = localTimezone in [&amp;#34;Asia/Shanghai&amp;#34;, &amp;#34;Asia/Urumqi&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    dateText = buildDateText()
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    dateText = encodeTimezone(dateText, timezoneHit)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    dateText = encodeDomainStatus(dateText, domainHit, labHit)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    send system prompt with dateText
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这段伪代码的重点不是具体函数名，而是顺序：先读本地环境，再做本地判断，最后把判断结果写回 prompt 文本。用户看到的是一次普通模型请求，服务端看到的则是带状态的请求。&lt;/p&gt;
&lt;h2 id=&#34;每一步到底检测了什么&#34;&gt;每一步到底检测了什么
&lt;/h2&gt;&lt;p&gt;把流程拆细一点，可以看到每一步拿到的信息并不复杂，但组合起来就能形成一个比较明确的环境画像。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;步骤&lt;/th&gt;
          &lt;th&gt;客户端读取什么&lt;/th&gt;
          &lt;th&gt;用来判断什么&lt;/th&gt;
          &lt;th&gt;结果写到哪里&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;读取环境变量&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;ANTHROPIC_BASE_URL&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;是否使用自定义 API endpoint&lt;/td&gt;
          &lt;td&gt;决定是否进入后续逻辑&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;解析 URL&lt;/td&gt;
          &lt;td&gt;hostname&lt;/td&gt;
          &lt;td&gt;endpoint 属于哪类域名&lt;/td&gt;
          &lt;td&gt;影响 apostrophe 码点&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;匹配名单&lt;/td&gt;
          &lt;td&gt;域名后缀、关键词&lt;/td&gt;
          &lt;td&gt;是否命中特定服务或 AI 实验室相关关键词&lt;/td&gt;
          &lt;td&gt;影响 apostrophe 码点&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;读取系统时区&lt;/td&gt;
          &lt;td&gt;本机 timezone&lt;/td&gt;
          &lt;td&gt;是否属于特定地区时区&lt;/td&gt;
          &lt;td&gt;影响日期分隔符&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;改写提示词&lt;/td&gt;
          &lt;td&gt;日期句子&lt;/td&gt;
          &lt;td&gt;把前面判断结果编码进去&lt;/td&gt;
          &lt;td&gt;system prompt&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;所以它不是靠单一信号判断。&lt;code&gt;ANTHROPIC_BASE_URL&lt;/code&gt; 只是入口，hostname 和时区才是后续分层依据，最后的 system prompt 才是承载结果的地方。&lt;/p&gt;
&lt;h2 id=&#34;触发点anthropic_base_url&#34;&gt;触发点：&lt;code&gt;ANTHROPIC_BASE_URL&lt;/code&gt;
&lt;/h2&gt;&lt;p&gt;公开分析里提到的第一步，是读取环境变量 &lt;code&gt;ANTHROPIC_BASE_URL&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这个变量通常用于让 Claude Code 不直接请求官方 &lt;code&gt;api.anthropic.com&lt;/code&gt;，而是走自定义 API endpoint、企业网关、代理服务或兼容 Anthropic API 的中转服务。也就是说，它本身不是恶意配置，很多团队会因为网络、合规、审计、统一鉴权或成本管理而使用自定义网关。&lt;/p&gt;
&lt;p&gt;据公开逆向分析称，相关逻辑并不是对所有请求无条件触发，而是在检测到配置了非默认 API base URL 后，才进入后续判断。这个设计很关键：它把“普通官方直连用户”和“使用自定义 endpoint 的用户”先分开，再对后者做更细的环境识别。&lt;/p&gt;
&lt;p&gt;从流程上看，大致可以拆成三层：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;是否设置了 &lt;code&gt;ANTHROPIC_BASE_URL&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;这个 URL 的 hostname 是否命中特定域名或关键词名单。&lt;/li&gt;
&lt;li&gt;本机时区是否属于特定地区。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这也是为什么争议会集中在 Claude Code 这类本地开发工具上。它运行在用户机器里，天然能读取本机环境变量、时区、配置文件和 shell 上下文。&lt;/p&gt;
&lt;h2 id=&#34;第一步提取自定义-endpoint-的-hostname&#34;&gt;第一步：提取自定义 endpoint 的 hostname
&lt;/h2&gt;&lt;p&gt;如果存在 &lt;code&gt;ANTHROPIC_BASE_URL&lt;/code&gt;，客户端可以把它解析成 URL，并取出 hostname。&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;https://example-gateway.com/v1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;真正参与匹配的通常不是完整 URL，而是：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;example-gateway.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这样做有两个效果。&lt;/p&gt;
&lt;p&gt;第一，路径、query string、具体接口版本都不重要，只看域名主体。第二，匹配逻辑可以复用一份域名或关键词名单，不必关心不同服务商把 API path 设计成什么样。&lt;/p&gt;
&lt;p&gt;公开讨论里提到，这份名单并不是明文平铺在代码里，而是经过混淆或编码存储，需要运行时解码后再匹配。这样的处理未必一定代表恶意，但它确实降低了普通用户通过搜索字符串发现规则的可能性。&lt;/p&gt;
&lt;h2 id=&#34;第二步检查系统时区&#34;&gt;第二步：检查系统时区
&lt;/h2&gt;&lt;p&gt;另一个被反复提到的信号，是系统时区。&lt;/p&gt;
&lt;p&gt;公开分析称，检测逻辑会关注类似下面的时区：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Asia/Shanghai
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Asia/Urumqi
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;时区是一个很微妙的信号。它不需要发起网络请求，也不需要读取 IP 地址，却能粗略反映用户所在地区或系统长期使用习惯。很多开发者即使切换了网络出口，也不会修改电脑时区，因为改时区会影响日历、日志、构建时间戳和日常使用。&lt;/p&gt;
&lt;p&gt;如果客户端只看 IP，用户可能会使用代理、企业出口或云环境；如果同时看本机时区，就多了一层本地环境判断。&lt;/p&gt;
&lt;h2 id=&#34;第三步匹配域名名单和关键词&#34;&gt;第三步：匹配域名名单和关键词
&lt;/h2&gt;&lt;p&gt;除了时区，公开分析还提到两类 hostname 匹配：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;是否命中特定域名、域名后缀或服务商名单。&lt;/li&gt;
&lt;li&gt;是否包含与 AI 实验室、模型服务或相关公司有关的关键词。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这类匹配的目标，可能不是简单判断“是不是中国用户”，而是把不同风险类型分层：普通自定义网关、地区相关 endpoint、AI 实验室相关 endpoint、二者同时命中。&lt;/p&gt;
&lt;p&gt;这也是后续编码需要多个状态的原因。如果只想表达“命中/未命中”，一个二值标记就够了；但公开分析里提到的 Unicode 字符替换，能表示更多组合状态。&lt;/p&gt;
&lt;h2 id=&#34;关键手法把结果写进-system-prompt&#34;&gt;关键手法：把结果写进 system prompt
&lt;/h2&gt;&lt;p&gt;争议最大的地方，是检测结果并不是作为显式字段上传，而是被编码进 system prompt 中的日期句子。&lt;/p&gt;
&lt;p&gt;Claude Code 请求里本来就会包含类似这样的系统信息：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Today&amp;#39;s date is 2026-06-30.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;公开分析称，客户端会在发送请求前对这句话做两类修改。&lt;/p&gt;
&lt;p&gt;第一类是日期分隔符。如果检测到特定时区，日期可能从：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2026-06-30
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;变成：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2026/06/30
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;第二类是替换 &lt;code&gt;Today&#39;s&lt;/code&gt; 里的 apostrophe。肉眼看起来都是一个撇号，但 Unicode 码点不同。例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;’
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ʼ
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ʹ
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;它们在显示层面非常接近，但在程序眼里是完全不同的字符。服务端只要检查 &lt;code&gt;Today&lt;/code&gt; 和 &lt;code&gt;s date is&lt;/code&gt; 中间那个字符的码点，就能读出客户端编码进去的状态。&lt;/p&gt;
&lt;p&gt;更直观地看，可以把它理解成一张“暗号表”：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;显示效果&lt;/th&gt;
          &lt;th&gt;Unicode&lt;/th&gt;
          &lt;th&gt;大致含义&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;&#39;&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;U+0027&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;普通 ASCII apostrophe，未命中特定条件&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;’&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;U+2019&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;命中某类域名或后缀名单&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;ʼ&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;U+02BC&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;命中 AI 实验室相关关键词&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;ʹ&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;U+02B9&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;多个条件同时命中&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;上面这张表不是为了教人绕过检测，而是说明它为什么不容易被肉眼发现：四个字符都像撇号，但码点完全不同。日志、终端、网页字体一渲染，差异会更不明显。&lt;/p&gt;
&lt;p&gt;这就是为什么很多人把它称为 prompt steganography，也就是把信息藏进看起来正常的提示词文本里。&lt;/p&gt;
&lt;h2 id=&#34;为什么这种标记比较隐蔽&#34;&gt;为什么这种标记比较隐蔽
&lt;/h2&gt;&lt;p&gt;这种做法隐蔽，主要有三个原因。&lt;/p&gt;
&lt;p&gt;第一，它不需要额外请求。网络抓包时，你不会看到一个单独的“上报环境信息”接口，请求仍然是正常的模型调用。&lt;/p&gt;
&lt;p&gt;第二，它不需要明显字段。即使你检查 JSON，也可能只看到普通 system prompt。除非逐字符比较 Unicode 码点，否则很容易忽略撇号差异。&lt;/p&gt;
&lt;p&gt;第三，它利用了用户对系统提示词的低关注度。开发者通常会关注自己输入的 prompt、工具调用参数、token 消耗和模型输出，很少逐字检查客户端自动拼进去的系统上下文。&lt;/p&gt;
&lt;p&gt;从工程角度看，这种机制很聪明；从信任角度看，它也很敏感。因为用户授权 Claude Code 运行在本地终端里，是为了让它读写代码、执行命令、协助开发，而不是为了让本地环境特征被隐藏编码进请求。&lt;/p&gt;
&lt;h2 id=&#34;服务端如何读取这类信息&#34;&gt;服务端如何读取这类信息
&lt;/h2&gt;&lt;p&gt;如果公开分析属实，服务端读取这类标记并不复杂。&lt;/p&gt;
&lt;p&gt;它只需要在接收请求后检查两件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;日期使用 &lt;code&gt;-&lt;/code&gt; 还是 &lt;code&gt;/&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Today&#39;s&lt;/code&gt; 中间的 apostrophe 是哪个 Unicode 字符。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;换成更接地气的说法：客户端发出去的是一张“正常表格”，但日期格式和撇号字体里夹了两个小纸条。&lt;/p&gt;
&lt;p&gt;例如正常请求可能长这样：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Today&amp;#39;s date is 2026-06-30.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;命中特定时区后，可能变成：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Today&amp;#39;s date is 2026/06/30.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;命中某类 endpoint 后，撇号可能变成另一个 Unicode 字符。肉眼看到的仍然像：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Today’s date is 2026-06-30.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;但程序读到的不是同一个 &lt;code&gt;&#39;&lt;/code&gt;。服务端不需要理解整句话，只要取出两个位置的字符，就能还原标记状态。&lt;/p&gt;
&lt;p&gt;然后就能把请求归类为不同状态。例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;没有命中特定名单。&lt;/li&gt;
&lt;li&gt;命中某类域名名单。&lt;/li&gt;
&lt;li&gt;命中 AI 实验室相关关键词。&lt;/li&gt;
&lt;li&gt;同时命中多个条件。&lt;/li&gt;
&lt;li&gt;系统时区属于特定地区。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也就是说，真正的“上报字段”并不叫 &lt;code&gt;region&lt;/code&gt;、&lt;code&gt;proxy&lt;/code&gt; 或 &lt;code&gt;risk_flag&lt;/code&gt;，而是藏在自然语言系统提示词里的字符差异。&lt;/p&gt;
&lt;h2 id=&#34;这和普通风控有什么区别&#34;&gt;这和普通风控有什么区别
&lt;/h2&gt;&lt;p&gt;服务商做风控并不奇怪。API 滥用、账号转售、模型蒸馏、异常并发和违反地区政策，都是现实问题。问题在于透明度和边界。&lt;/p&gt;
&lt;p&gt;普通风控通常更容易被理解：登录 IP、支付地区、请求频率、设备信息、组织账号、API key 使用模式。这些信号虽然也敏感，但用户大致能预期平台会用它们做安全判断。&lt;/p&gt;
&lt;p&gt;而本次争议点在于：如果客户端确实把本机时区、自定义 endpoint 命中结果等信息隐藏编码进 system prompt，用户很难从产品界面、请求字段或 release notes 里知道这件事。对一个拥有文件系统和 shell 权限的开发工具来说，这会直接影响信任。&lt;/p&gt;
&lt;h2 id=&#34;开发者可以关注什么&#34;&gt;开发者可以关注什么
&lt;/h2&gt;&lt;p&gt;这类事件给开发者的启发，不是简单地“不要用某个工具”，而是要重新看待本地 Agent 的信任边界。&lt;/p&gt;
&lt;p&gt;可以关注几件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;本地 CLI 是否会读取环境变量、配置文件、时区、系统语言等环境信息。&lt;/li&gt;
&lt;li&gt;自动拼接的 system prompt 是否可查看、可导出、可审计。&lt;/li&gt;
&lt;li&gt;请求发送前的最终 payload 是否能被用户检查。&lt;/li&gt;
&lt;li&gt;release notes 是否说明了新增的检测、风控或遥测逻辑。&lt;/li&gt;
&lt;li&gt;企业网关是否能记录和审计上游请求内容。&lt;/li&gt;
&lt;li&gt;本地工具是否提供关闭遥测或敏感环境读取的选项。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果一个 AI 编程工具能读写项目文件、执行 shell 命令、访问 git、读取环境变量，那么它本质上已经接近“高权限本地代理”。这类工具的透明度标准，应该比普通聊天网页更高。&lt;/p&gt;
&lt;h2 id=&#34;结论&#34;&gt;结论
&lt;/h2&gt;&lt;p&gt;这次 Claude Code 请求水印争议，真正值得讨论的不是某个账号是否被封，而是客户端如何把环境信息编码进正常请求。&lt;/p&gt;
&lt;p&gt;按公开逆向分析的说法，这套流程大致是：读取 &lt;code&gt;ANTHROPIC_BASE_URL&lt;/code&gt;，解析 hostname，匹配域名和关键词名单，检查系统时区，再通过日期分隔符和 Unicode apostrophe 把结果写进 system prompt。整个过程不需要额外网络请求，也不需要新增显式字段。&lt;/p&gt;
&lt;p&gt;如果这只是反滥用风控的一部分，服务商也应该用更透明的方式说明边界。开发者给本地 Agent 的权限越大，越需要知道它到底读取了什么、改写了什么、发送了什么。&lt;/p&gt;
&lt;p&gt;AI 编程工具的能力越强，信任问题就越不能只靠“默认相信”解决。&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
