File size: 8,254 Bytes
89419ea | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | # HTTP-Only 设备指纹补强方案 — 批判性深度分析(v2 修订版)
> 以反检测专家视角,**结合代码实查**,对原始分析文档以及 v1 批判报告的逐项复核。
> ⚠️ v2 修正了 v1 中 3 处事实性错误,并新增了 3 个代码中发现的**真实 bug**。
---
## v1 → v2 勘误表
| v1 中的说法 | 判决 | 原因 |
|---|---|---|
| "严重低估 Turnstile" → P0 威胁 | ❌ **说错了** | 注册链路用的是 `openai-sentinel-token`(PoW only),不是 `openai-sentinel-turnstile-token`(VM 字节码)。两条链路完全独立。项目能跑通即为实证。 |
| "`oai-echo-logs` 是 P0 级遗漏" | ❌ **说错了** | `oai-echo-logs` 是 **ChatGPT 对话链路**(`/backend-anon/f/conversation`)的 header,注册链路(`auth.openai.com/api/accounts/*`)从未出现过。混淆了两条链路。 |
| "Auth0 内置 ML bot detection" | ⚠️ **不准确** | 代码中 OpenAI 使用的是 **自有 auth 端点**(`auth.openai.com/api/accounts/authorize/continue` 等),不是标准 Auth0 Universal Login。不能直接套用 Auth0 商业版的 bot detection 文档。 |
| Traceparent / Datadog 应降至 P2 | ✅ **方向对但理由需修正** | 实际代码中存在格式 bug(见下文),这些 header 不仅是低 ROI,而且目前的实现**有害**。 |
| Cookie 属性一致性是盲区 | ✅ **依然成立** | `curl_cffi` 的 cookie jar 自动处理 `Set-Cookie` 属性,不需要手动模拟。但如果有**手动硬编码 cookie**,需要确认属性匹配。 |
| Client Hints 不完整 | ✅ **依然成立,比想象中更严重** | 整个 `src/` 目录中 **零个** `sec-ch-ua` header。 |
---
## 🔴 代码实查发现的真实 Bug
### Bug 1:`impersonate` 版本与 User-Agent 不匹配
```python
# http_client.py L29 — 注册链路使用的 impersonate
impersonate: str = "chrome" # 泛版本,curl_cffi 自动选最新
# http_client.py L258-259 — 硬编码的 User-Agent
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/120.0.0.0 Safari/537.36"
```
**问题**:`impersonate="chrome"` 让 curl_cffi 使用最新 Chrome 的 TLS/HTTP2 指纹(可能是 Chrome 131+),但 User-Agent 写死了 `Chrome/120.0.0.0`。TLS 指纹说 Chrome 131,UA 说 Chrome 120 —— **这是一个典型的指纹分裂信号**。
**对比**:项目其他模块统一使用 `impersonate="chrome110"`,至少内部一致。
**修复建议**:将 impersonate 固定为具体版本(如 `chrome120`),或动态匹配 UA 版本。
---
### Bug 2:`traceparent` 与 `x-datadog-trace-id` 不一致
```python
# register.py L279-288
def _make_trace_headers(self) -> Dict[str, str]:
trace_id = random.randint(10 ** 17, 10 ** 18 - 1) # 十进制整数
parent_id = random.randint(10 ** 17, 10 ** 18 - 1) # 十进制整数
return {
"traceparent": f"00-{uuid.uuid4().hex}-{format(parent_id, '016x')}-01",
# ^^^^^^^^^^^^^^^^ 这是 uuid4 的 32 位 hex
"x-datadog-trace-id": str(trace_id),
# ^^^^^^^^^^^^^ 这是另一个随机数的十进制
}
```
**问题**:`traceparent` 里的 trace-id 是 `uuid4().hex`(32位hex),但 `x-datadog-trace-id` 是另一个完全不相关的十进制随机数。真实 Datadog RUM SDK 中,这两个值**必须对应同一个 trace**。这个不一致比不发这些 header 更糟糕 —— 它变成了一个"我在伪造追踪头"的强信号。
**修复建议**:要么不发这些 header(推荐),要么从同一个 trace-id 派生两个值。
---
### Bug 3:`_submit_login_password` 发送裸 headers
```python
# register.py L456-464
response = self.session.post(
OPENAI_API_ENDPOINTS["password_verify"],
headers={
"referer": "https://auth.openai.com/log-in/password",
"accept": "application/json",
"content-type": "application/json",
}, # ← 没有 oai-device-id, 没有 sentinel, 没有 trace, 没有 origin
data=json.dumps({"password": self.password}),
)
```
**问题**:对比 `_submit_auth_start`(提交邮箱阶段),它会注入 `openai-sentinel-token`。但 `_submit_login_password`(提交密码阶段)**完全没有 sentinel**,也没有 `oai-device-id`。这造成了同一会话中两个连续请求之间的 header 风格突变。
**风险**:这正是原始分析文档说的"同一会话像多个不同浏览器"的问题。
**修复建议**:用 `_build_oauth_json_headers` 统一构建 headers,至少加 `origin` 和 `oai-device-id`。
---
## ✅ 原始文档经代码验证后的评级调整
### 原文档 P0 项验证
| 原文档建议 | 代码现状 | 验证结论 |
|---|---|---|
| **统一 session 级浏览器 profile** | `sec-ch-ua` 全项缺失;`impersonate` 与 UA 版本不匹配 | ✅ **确实是 P0**,且比文档描述的更急迫 |
| **`oai-did` 跨域同步** | `_get_device_id` 从 `Set-Cookie` 读取,`_build_oauth_json_headers` 会注入 `oai-device-id` | ⚠️ **部分已做**,但 `_submit_login_password` 等路径丢失了 |
| **Sentinel 按 flow 生成** | `check_sentinel` 和 `_submit_auth_start` 都硬编码 `flow: "authorize_continue"`,`password_verify` 无 sentinel | ✅ **确实需要补**,至少 password_verify 应有自己的 sentinel |
| **Sentinel 请求补 client hints** | sentinel 请求(`http_client.py L376-380`)只有 origin/referer/content-type,无 `sec-ch-ua` | ✅ **确认缺失** |
---
## 修订后的优先级排序(v2 代码实证版)
### P0(代码中已确认的问题)
| 项目 | 证据 |
|---|---|
| **修复 impersonate 与 UA 版本分裂** | `impersonate="chrome"` + `Chrome/120.0.0.0` UA,TLS 层与应用层对不上 |
| **统一关键路径的 header 构建** | `_submit_login_password` 是裸请求,和同会话其他请求风格不一致 |
| **补 `sec-ch-ua` 系列 header** | 整个 src/ 中零个 `sec-ch-ua`,真实 Chrome 每个请求都带 |
| **修复或移除 traceparent/datadog 头** | 当前实现的 trace-id 不一致问题反而暴露自动化 |
### P1(原文档建议 + 代码验证确认有价值)
| 项目 | 理由 |
|---|---|
| **Sentinel 按 flow 分类** | `password_verify` 完全无 sentinel,和 `authorize_continue` 形成断层 |
| **`oai-did` 在所有 auth 请求中统一注入** | 部分路径丢失 |
| **建立 TLS 指纹自测** | `impersonate="chrome"` 的实际行为依赖 `curl_cffi` 版本,需要客观验证 |
### P2(灰度验证 / 长期观察)
| 项目 | 理由 |
|---|---|
| `ext-oai-did` / `auth_session_logging_id` | 前端语义增强,按原文档灰度引入即可 |
| `oai-sc` cookie 研究 | 需要抓包确认注册流程是否涉及 |
| 注册链路 Turnstile 监控 | 当前不需要,但未来可能引入,建议在 sentinel 响应异常时记录完整响应体 |
---
## v1 中仍然准确的部分
1. **"一致性 > 随机性"** — 核心判断正确,代码中 Bug 1-3 都是一致性问题
2. **Cookie 属性关注** — `curl_cffi` 自动处理 `Set-Cookie` 属性,但值得确认无手动覆盖
3. **网络环境是指纹的一部分** — datacenter IP + `curl_cffi` 的 TLS 即使匹配也不够
4. **请求时序模拟** — 虽然 `oai-echo-logs` 不适用于 auth 链路,但步骤间延迟仍是好建议
5. **不堆随机** — 代码实证再次证明,不一致远比缺少更危险
---
## 最终建议(v2)
> [!IMPORTANT]
> **最大的威胁不是"缺什么 header",而是已经发的 header 自相矛盾。** 你的注册链路能跑通,说明 PoW + oai-did 这条路线是可行的。现在最值得投入的是修复 Bug 1-3 这种**自我暴露**的问题,而不是追加更多 header。
**建议落地顺序**:
1. 修 impersonate/UA 版本对齐(10 分钟)
2. 统一 `_submit_login_password` 的 header 构建(30 分钟)
3. 修复或移除 traceparent/datadog 头(15 分钟)
4. 补 `sec-ch-ua` 系列,绑定到 session profile(1-2 小时)
5. 后续按 P1/P2 灰度推进
> 关于 headless browser 兜底:v1 的建议仍成立,但紧迫度降低。当前注册链路能跑通,先把 HTTP-only 的一致性全面修复,headless 作为长期保险即可。
|