<?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/zh-tw/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-tw</language>
        <lastBuildDate>Wed, 01 Jul 2026 21:15:26 +0800</lastBuildDate><atom:link href="https://knightli.com/zh-tw/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/zh-tw/2026/07/01/claude-code-prompt-steganography-detection/</link>
        <pubDate>Wed, 01 Jul 2026 21:15:26 +0800</pubDate>
        
        <guid>https://knightli.com/zh-tw/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>
