最近圍繞 Claude Code 有一輪爭議:有逆向分析者稱,某些版本的 Claude Code 會在特定條件下讀取本機環境資訊,並把檢測結果編碼進發往服務端的 system prompt 裡。這個機制不是額外上傳一個明顯的 telemetry 欄位,而是修改一行看起來很普通的日期提示,例如 Today's date is 2026-06-30.。
公開討論裡最值得看的,不是「封號」這個結果,而是檢測流程本身。它展示了一個本地 AI 編程工具如何在不增加額外網路請求的情況下,把客戶端環境特徵夾帶進正常請求。對開發者來說,這比單純的帳號風險更值得警惕。
本文只整理目前公開逆向文章和社群討論中反覆出現的技術細節。由於這類分析依賴具體版本和逆向結果,下面內容應理解為「公開報告所稱的實作邏輯」,不等同於官方完整說明。
先把流程說白
這套邏輯如果用一句話概括,就是:先看你有沒有走自訂 API 地址,再看這個地址像不像特定清單裡的服務,再看你電腦時區,最後把判斷結果藏進一句日期提示裡。
按公開分析裡的說法,鏈路大概是這樣:
- Claude Code 啟動或發請求前,讀取
ANTHROPIC_BASE_URL。 - 如果這個變數不存在,或者仍然指向官方預設地址,後面的特殊標記邏輯可能不會觸發。
- 如果它指向自訂 endpoint,就解析 URL,取出 hostname。
- 客戶端把 hostname 拿去和內建清單、關鍵字規則做匹配。
- 同時讀取本機時區,判斷是否是
Asia/Shanghai或Asia/Urumqi這類目標時區。 - 客戶端不新增單獨欄位,而是修改 system prompt 裡的日期句子。
- 服務端收到請求後,透過日期分隔符和撇號碼點,讀出客戶端環境標記。
這個過程最繞的地方在第 6 步。它不是寫一個很明顯的欄位,比如:
|
|
而是把資訊塞進這種看起來很正常的文字裡:
|
|
普通人掃一眼只會覺得這是日期。真正傳遞資訊的,是日期裡的 / 或 -,以及 Today's 中間那個看起來幾乎一樣的撇號。
如果寫成偽代碼,邏輯大概像這樣:
|
|
這段偽代碼的重點不是具體函式名,而是順序:先讀本地環境,再做本地判斷,最後把判斷結果寫回 prompt 文字。使用者看到的是一次普通模型請求,服務端看到的則是帶狀態的請求。
每一步到底檢測了什麼
把流程拆細一點,可以看到每一步拿到的資訊並不複雜,但組合起來就能形成一個比較明確的環境画像。
| 步驟 | 客戶端讀取什麼 | 用來判斷什麼 | 結果寫到哪裡 |
|---|---|---|---|
| 讀取環境變數 | ANTHROPIC_BASE_URL |
是否使用自訂 API endpoint | 決定是否進入後續邏輯 |
| 解析 URL | hostname | endpoint 屬於哪類域名 | 影響 apostrophe 碼點 |
| 匹配清單 | 域名後綴、關鍵字 | 是否命中特定服務或 AI 實驗室相關關鍵字 | 影響 apostrophe 碼點 |
| 讀取系統時區 | 本機 timezone | 是否屬於特定地區時區 | 影響日期分隔符 |
| 改寫提示詞 | 日期句子 | 把前面判斷結果編碼進去 | system prompt |
所以它不是靠單一信號判斷。ANTHROPIC_BASE_URL 只是入口,hostname 和時區才是後續分層依據,最後的 system prompt 才是承載結果的地方。
觸發點:ANTHROPIC_BASE_URL
公開分析裡提到的第一步,是讀取環境變數 ANTHROPIC_BASE_URL。
這個變數通常用於讓 Claude Code 不直接請求官方 api.anthropic.com,而是走自訂 API endpoint、企業閘道、代理服務或相容 Anthropic API 的中轉服務。也就是說,它本身不是惡意配置,很多團隊會因為網路、合規、審計、統一鑑權或成本管理而使用自訂閘道。
據公開逆向分析稱,相關邏輯並不是對所有請求無條件觸發,而是在檢測到配置了非預設 API base URL 後,才進入後續判斷。這個設計很關鍵:它把「普通官方直連使用者」和「使用自訂 endpoint 的使用者」先分開,再對後者做更細的環境識別。
從流程上看,大致可以拆成三層:
- 是否設定了
ANTHROPIC_BASE_URL。 - 這個 URL 的 hostname 是否命中特定域名或關鍵字清單。
- 本機時區是否屬於特定地區。
這也是為什麼爭議會集中在 Claude Code 這類本地開發工具上。它運行在使用者機器裡,天然能讀取本機環境變數、時區、設定檔和 shell 上下文。
第一步:提取自訂 endpoint 的 hostname
如果存在 ANTHROPIC_BASE_URL,客戶端可以把它解析成 URL,並取出 hostname。
例如:
|
|
真正參與匹配的通常不是完整 URL,而是:
|
|
這樣做有兩個效果。
第一,路徑、query string、具體介面版本都不重要,只看域名主體。第二,匹配邏輯可以復用一份域名或關鍵字清單,不必關心不同服務商把 API path 設計成什麼樣。
公開討論裡提到,這份清單並不是明文平鋪在程式碼裡,而是經過混淆或編碼儲存,需要執行時解碼後再匹配。這樣的處理未必一定代表惡意,但它確實降低了普通使用者透過搜尋字串發現規則的可能性。
第二步:檢查系統時區
另一個被反覆提到的信號,是系統時區。
公開分析稱,檢測邏輯會關注類似下面的時區:
|
|
時區是一個很微妙的信號。它不需要發起網路請求,也不需要讀取 IP 地址,卻能粗略反映使用者所在地區或系統長期使用習慣。很多開發者即使切換了網路出口,也不會修改電腦時區,因為改時區會影響日曆、日誌、構建時間戳和日常使用。
如果客戶端只看 IP,使用者可能會使用代理、企業出口或雲環境;如果同時看本機時區,就多了一層本地環境判斷。
第三步:匹配域名清單和關鍵字
除了時區,公開分析還提到兩類 hostname 匹配:
- 是否命中特定域名、域名後綴或服務商清單。
- 是否包含與 AI 實驗室、模型服務或相關公司有關的關鍵字。
這類匹配的目標,可能不是簡單判斷「是不是中國使用者」,而是把不同風險類型分層:普通自訂閘道、地區相關 endpoint、AI 實驗室相關 endpoint、二者同時命中。
這也是後續編碼需要多個狀態的原因。如果只想表達「命中/未命中」,一個二值標記就夠了;但公開分析裡提到的 Unicode 字元替換,能表示更多組合狀態。
關鍵手法:把結果寫進 system prompt
爭議最大的地方,是檢測結果並不是作為顯式欄位上傳,而是被編碼進 system prompt 中的日期句子。
Claude Code 請求裡本來就會包含類似這樣的系統資訊:
|
|
公開分析稱,客戶端會在發送請求前對這句話做兩類修改。
第一類是日期分隔符。如果檢測到特定時區,日期可能從:
|
|
變成:
|
|
第二類是替換 Today's 裡的 apostrophe。肉眼看起來都是一個撇號,但 Unicode 碼點不同。例如:
|
|
它們在顯示層面非常接近,但在程式眼裡是完全不同的字元。服務端只要檢查 Today 和 s date is 中間那個字元的碼點,就能讀出客戶端編碼進去的狀態。
更直觀地看,可以把它理解成一張「暗號表」:
| 顯示效果 | Unicode | 大致含義 |
|---|---|---|
' |
U+0027 |
普通 ASCII apostrophe,未命中特定條件 |
’ |
U+2019 |
命中某類域名或後綴清單 |
ʼ |
U+02BC |
命中 AI 實驗室相關關鍵字 |
ʹ |
U+02B9 |
多個條件同時命中 |
上面這張表不是為了教人繞過檢測,而是說明它為什麼不容易被肉眼發現:四個字元都像撇號,但碼點完全不同。日誌、終端、網頁字體一渲染,差異會更不明顯。
這就是為什麼很多人把它稱為 prompt steganography,也就是把資訊藏進看起來正常的提示詞文字裡。
為什麼這種標記比較隱蔽
這種做法隱蔽,主要有三個原因。
第一,它不需要額外請求。網路抓包時,你不會看到一個單獨的「上報環境資訊」介面,請求仍然是正常的模型呼叫。
第二,它不需要明顯欄位。即使你檢查 JSON,也可能只看到普通 system prompt。除非逐字元比較 Unicode 碼點,否則很容易忽略撇號差異。
第三,它利用了使用者對系統提示詞的低關注度。開發者通常會關注自己輸入的 prompt、工具呼叫參數、token 消耗和模型輸出,很少逐字檢查客戶端自動拼進去的系統上下文。
從工程角度看,這種機制很聰明;從信任角度看,它也很敏感。因為使用者授權 Claude Code 運行在本地終端裡,是為了讓它讀寫程式碼、執行命令、協助開發,而不是為了讓本地環境特徵被隱藏編碼進請求。
服務端如何讀取這類資訊
如果公開分析屬實,服務端讀取這類標記並不複雜。
它只需要在接收請求後檢查兩件事:
- 日期使用
-還是/。 Today's中間的 apostrophe 是哪個 Unicode 字元。
換成更接地氣的說法:客戶端發出去的是一張「正常表格」,但日期格式和撇號字體裡夾了兩個小紙條。
例如正常請求可能長這樣:
|
|
命中特定時區後,可能變成:
|
|
命中某類 endpoint 後,撇號可能變成另一個 Unicode 字元。肉眼看到的仍然像:
|
|
但程式讀到的不是同一個 '。服務端不需要理解整句話,只要取出兩個位置的字元,就能還原標記狀態。
然後就能把請求歸類為不同狀態。例如:
- 沒有命中特定清單。
- 命中某類域名清單。
- 命中 AI 實驗室相關關鍵字。
- 同時命中多個條件。
- 系統時區屬於特定地區。
也就是說,真正的「上報欄位」並不叫 region、proxy 或 risk_flag,而是藏在自然語言系統提示詞裡的字元差異。
這和普通風控有什麼區別
服務商做風控並不奇怪。API 濫用、帳號轉售、模型蒸餾、異常並發和違反地區政策,都是現實問題。問題在於透明度和邊界。
普通風控通常更容易被理解:登入 IP、支付地區、請求頻率、裝置資訊、組織帳號、API key 使用模式。這些信號雖然也敏感,但使用者大致能預期平台會用它們做安全判斷。
而本次爭議點在於:如果客戶端確實把本機時區、自訂 endpoint 命中結果等資訊隱藏編碼進 system prompt,使用者很難從產品介面、請求欄位或 release notes 裡知道這件事。對一個擁有檔案系統和 shell 權限的開發工具來說,這會直接影響信任。
開發者可以關注什麼
這類事件給開發者的啟發,不是簡單地「不要用某個工具」,而是要重新看待本地 Agent 的信任邊界。
可以關注幾件事:
- 本地 CLI 是否會讀取環境變數、設定檔、時區、系統語言等環境資訊。
- 自動拼接的 system prompt 是否可查看、可匯出、可審計。
- 請求發送前的最終 payload 是否能被使用者檢查。
- release notes 是否說明了新增的檢測、風控或遙測邏輯。
- 企業閘道是否能記錄和審計上游請求內容。
- 本地工具是否提供關閉遙測或敏感環境讀取的選項。
如果一個 AI 編程工具能讀寫專案檔案、執行 shell 命令、訪問 git、讀取環境變數,那它本質上已經接近「高權限本地代理」。這類工具的透明度標準,應該比普通聊天網頁更高。
結論
這次 Claude Code 請求水印爭議,真正值得討論的不是某個帳號是否被封,而是客戶端如何把環境資訊編碼進正常請求。
按公開逆向分析的說法,這套流程大致是:讀取 ANTHROPIC_BASE_URL,解析 hostname,匹配域名和關鍵字清單,檢查系統時區,再透過日期分隔符和 Unicode apostrophe 把結果寫進 system prompt。整個過程不需要額外網路請求,也不需要新增顯式欄位。
如果這只是反濫用風控的一部分,服務商也應該用更透明的方式說明邊界。開發者給本地 Agent 的權限越大,越需要知道它到底讀取了什麼、改寫了什麼、發送了什麼。
AI 編程工具的能力越強,信任問題就越不能只靠「預設相信」解決。