反编译本地 Claude Code 2.1.145 二进制 (Bun 1.3.14 打包, @anthropic-ai/sdk@0.94.0 嵌入) 提取真实指纹,系统性升级 mimicry。 核心改动: - 新增 ClaudeCodeBundle struct 作为单一事实源,DefaultBundle 描述当前 伪装目标的完整快照 (CLIVersion/SDKVersion/RuntimeVersion/OS/Arch) - DefaultCLIVersion/DefaultStainlessPackageVersion/CLICurrentVersion/ DefaultHeaders 全部派生自 DefaultBundle,消除三处 (2.1.92, 2.1.104, 0.70.0, 0.81.0) 版本分裂 - CLI 版本 2.1.92/2.1.104 -> 2.1.145 - SDK 版本 0.70.0/0.81.0 -> 0.94.0 - 新增 12 个 2.1.145 反编译确认的 anthropic-beta token: advanced-tool-use, tool-search-tool, mcp-servers, mcp-client, mid-conversation-system, afk-mode, cache-diagnosis, context-hint, environments, managed-agents, skills, compact - FullClaudeCodeMimicryBetas() 从 7 个 token 升级到 21 个 ordered list - 修正 BetaTokenEfficientTools 错日期 (2026-03-28 -> 2025-02-19) - 从默认 beta header 移除已 GA 的 BetaFineGrainedToolStreaming / BetaTokenEfficientTools (常量保留供客户端显式 merge) - claudemask.RequiredNodeHeaders 加 X-Claude-Code-Session-Id 强制 新增 ensureClaudeCodeSessionID helper (claude_code_session_id.go): - 真实 CLI 在 SDK 内强制 X-Claude-Code-Session-Id:y_(),缺失被判第三方 - OAuth mimic 路径: metadata.user_id 派生 -> canonical UUID 写入 -> 兜底 uuid.NewString() - API key passthrough 路径: 不从 body 派生,保护客户端原始语义 - 所有路径均对客户端传入的非法 UUID 执行删除 (避免恶意值上游透传) - 所有写入 header 的 session-id 都通过 uuid.Parse 校验 测试: - 新增 14 个 ensureClaudeCodeSessionID 单元测试,含恶意 UUID 注入拒绝 + API key 路径隔离 + canonical 形式校验 - 新增 3 个 bundle 派生一致性测试 - mask_test 加 session-id 缺失校验 case - 老 UA 断言 2.1.104 -> 2.1.145 不在范围: - TLS 指纹 (utls 已处理) - Bun.hash vs xxHash64 算法验证 (需 golden vectors,独立项目) References: - VERSION:2.1.145 BUILD_TIME:2026-05-19T01:36:35Z GIT_SHA:daa4c3755d45ab0cf97bb41db8c03bd2dfd2ff5f
74 lines
2.7 KiB
Go
74 lines
2.7 KiB
Go
package service
|
||
|
||
import (
|
||
"net/http"
|
||
|
||
"github.com/google/uuid"
|
||
"github.com/tidwall/gjson"
|
||
)
|
||
|
||
// ensureClaudeCodeSessionID 确保 X-Claude-Code-Session-Id header 被合理填充。
|
||
//
|
||
// 真实 Claude Code 2.1.145 CLI 在 SDK 内会始终设置 X-Claude-Code-Session-Id 为
|
||
// 当前 CLI 会话的 UUID(源码:`"X-Claude-Code-Session-Id":y_()`)。上游若发现
|
||
// 该头缺失,可能将请求识别为非官方 Claude Code 第三方调用。
|
||
//
|
||
// 行为按 tokenType / mimicClaudeCode 分两条路径:
|
||
//
|
||
// OAuth mimic 路径 (tokenType == "oauth" && mimicClaudeCode):
|
||
// 1. body 中 metadata.user_id 派生的 SessionID 是合法 UUID → canonicalize 写入
|
||
// 2. 请求 header 中已有合法 UUID → canonicalize 保留
|
||
// 3. 否则 → 兜底生成 UUID
|
||
//
|
||
// API key 透传 / 非 mimic 路径:
|
||
// - 不从 body 合成 header(避免污染客户端原始语义)
|
||
// - 但若客户端在 header 中传入了 X-Claude-Code-Session-Id:
|
||
// 合法 UUID → canonicalize 保留
|
||
// 非法值 → 删除(不向上游转发恶意值,符合 UUID 校验承诺)
|
||
// - 不兜底生成
|
||
//
|
||
// 安全说明:metadata.user_id 由客户端控制,ParseMetadataUserID 的正则仅约束字符集,
|
||
// 不保证 UUID 结构。因此所有写入 header 的 session id 都必须经 uuid.Parse 校验。
|
||
func ensureClaudeCodeSessionID(req *http.Request, body []byte, tokenType string, mimicClaudeCode bool) {
|
||
if req == nil {
|
||
return
|
||
}
|
||
if req.Header == nil {
|
||
req.Header = make(http.Header)
|
||
}
|
||
|
||
isOAuthMimic := tokenType == "oauth" && mimicClaudeCode
|
||
|
||
// OAuth mimic 路径:从 metadata 派生(仅在 mimic 场景写 header)。
|
||
if isOAuthMimic {
|
||
if uid := gjson.GetBytes(body, "metadata.user_id").String(); uid != "" {
|
||
if parsed := ParseMetadataUserID(uid); parsed != nil {
|
||
if id, err := uuid.Parse(parsed.SessionID); err == nil {
|
||
setHeaderRaw(req.Header, "X-Claude-Code-Session-Id", id.String())
|
||
return
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 处理客户端已传入的 header:合法 → canonicalize,非法 → 删除。
|
||
// 这条规则对所有路径生效,避免恶意非 UUID 值向上游透传。
|
||
if existing := getHeaderRaw(req.Header, "X-Claude-Code-Session-Id"); existing != "" {
|
||
if id, err := uuid.Parse(existing); err == nil {
|
||
canonical := id.String()
|
||
if canonical != existing {
|
||
setHeaderRaw(req.Header, "X-Claude-Code-Session-Id", canonical)
|
||
}
|
||
return
|
||
}
|
||
// 非法 UUID:删除避免透传
|
||
req.Header.Del("X-Claude-Code-Session-Id")
|
||
}
|
||
|
||
// OAuth mimic 兜底生成(仅 mimic 场景;API key 不污染)。
|
||
// uuid.NewString() 走 crypto/rand。
|
||
if isOAuthMimic {
|
||
setHeaderRaw(req.Header, "X-Claude-Code-Session-Id", uuid.NewString())
|
||
}
|
||
}
|