382 lines
22 KiB
Markdown
382 lines
22 KiB
Markdown
# 最终请求验证与 TLS 指纹兼容
|
||
|
||
当新 case 需要最终发送真实请求、交付 `final.js` / `final.py`,或用户提到 CycleTLS、impers、curl-cffi-node、curl_cffi、cffi_curl、cyCronet 时读取本文件。TLS 指纹兼容客户端的选择应从前置阶段开始确认,不要等普通 `fetch` / `requests` 失败后才临时切换;最终请求一律使用 Session 模式。
|
||
|
||
## 使用边界
|
||
|
||
- 只用于用户授权范围内的网页端 JS 补环境结果验证。
|
||
- 只做低频、最小化请求,优先复现用户已提供的成功 cURL / HAR。
|
||
- TLS 指纹兼容只解决普通 HTTP 客户端与浏览器在 TLS ClientHello、ALPN、HTTP/2、JA3/JA4、Cronet / curl-impersonate 网络栈上的差异;不能替代登录态、验证码、一次性 token、设备校验或业务授权。
|
||
- 最终真实请求必须写入一体化 `final.js` 或 `final.py`,由 Node.js / Python TLS 指纹兼容 Session 客户端直接发起;不得生成加密参数后再使用 ruyiPage、Playwright、Puppeteer、Selenium、CloakBrowser 或其他浏览器自动化验证。
|
||
- 如果目标 JS 运行时调用 `XMLHttpRequest` / `fetch` / `sendBeacon` 访问动态资源、Cookie、challenge、telemetry 或目标接口,JS 侧网络 API 也必须通过同一 TLS 指纹兼容 Session 的 live session bridge 发送;fixture/mock/default 200 只能作为离线诊断,不能作为 TLS 验证。
|
||
|
||
## 前置阶段必须选择客户端
|
||
|
||
在信息完整性检查和任务确认时,加入以下字段:
|
||
|
||
```markdown
|
||
- 最终请求 TLS 指纹兼容客户端:Node.js CycleTLS / Node.js impers / Node.js curl-cffi / curl-cffi-node / Python curl_cffi / Python cffi_curl / Python cyCronet / 不发真实请求
|
||
- 最终请求 Session 模式:一律启用 / 不发真实请求
|
||
- 是否已安装:是 / 否 / 待检测
|
||
- 若未安装:安装该客户端 / 改选其他客户端 / 不发真实请求
|
||
```
|
||
|
||
选择后立即检测:
|
||
|
||
```bash
|
||
node scripts/check_tls_clients.js --markdown
|
||
node scripts/check_tls_clients.js --python python --markdown
|
||
```
|
||
|
||
如果用户选择的库未安装,不要默认退回普通 `fetch` / `requests` 发真实请求。应让用户确认安装、改选其他已安装客户端,或选择“不发真实请求,只输出本地 sign / 参数”。
|
||
|
||
## 工具选择
|
||
|
||
| 运行环境 | 可选工具 | 适用场景 |
|
||
|---|---|---|
|
||
| Node.js | CycleTLS / cycletls / @luminati-io/cycletls | 需要在 Node.js `final.js` 中指定 JA3 / User-Agent / Header 并发起少量验证请求 |
|
||
| Node.js | impers | Node.js curl-impersonate 路线;需要浏览器 TLS / JA3 / HTTP2/HTTP3 指纹时优先考虑 |
|
||
| Node.js | curl-cffi-node / curl-cffi | Node.js 侧 curl_cffi / curl-impersonate 路线;需要用 Node.js 维持 session 与 Cookie jar 时可选 |
|
||
| Python | curl_cffi / cffi_curl | Python curl-impersonate 路线;常用于 Chrome / Firefox / Safari impersonate |
|
||
| Python | cyCronet / cycronet | Python Cronet-Cloak / Chromium 网络栈路线;适合需要 Chrome 系网络栈特征时 |
|
||
| 不发真实请求 | 无 | 只交付本地参数生成结果,不进行最终接口验证 |
|
||
|
||
注意:不同机器安装包名、导入名、版本和 API 可能不同。先运行检测脚本,不要在 Skill 中硬编码本机路径或版本。库 API 变动时,以本机安装版本的 README / 官方文档为准。
|
||
|
||
## 安装提示
|
||
|
||
只在用户确认后安装;不要在未确认时自动安装依赖。
|
||
|
||
```bash
|
||
# Node.js
|
||
npm install cycletls
|
||
npm install @luminati-io/cycletls
|
||
npm install impers
|
||
npm install curl-cffi
|
||
|
||
# Python
|
||
python -m pip install curl_cffi
|
||
python -m pip install cffi_curl
|
||
python -m pip install cycronet
|
||
```
|
||
|
||
## 高强度网络指纹一致性
|
||
|
||
## 用户 cURL 与取证 baseline 的网络指纹优先级
|
||
|
||
用户提供的 cURL / HAR 不一定来自最终取证浏览器。最终请求代码必须优先对齐“已确认取证 baseline”,而不是无条件复用用户 cURL 中的 UA、Client Hints、Header、Cookie 或 TLS 线索。
|
||
|
||
硬性规则:
|
||
|
||
- 前置阶段必须识别用户 cURL 样本浏览器族和取证 baseline 浏览器族。
|
||
- 如果两者一致,仍需确认 Header、TLS / HTTP2、Cookie jar 和代理 / 地区是否属于同一会话链路。
|
||
- 如果两者不一致,默认以取证 baseline 为准;cURL 只作为请求结构、参数位置、业务字段和历史现象线索。
|
||
- Chrome cURL 与 Firefox 取证冲突时,不得在 Firefox baseline 的最终请求中保留 Chrome `sec-ch-ua`、`sec-ch-ua-platform`、`sec-ch-ua-mobile`,也不得只修改 UA 后继续使用 Chrome TLS / HTTP2 profile。
|
||
- 最终 `final.js` / `final.py` 的 UA、Accept-Language、Client Hints、Header 顺序、TLS / JA3 / JA4、HTTP/2 Akamai 指纹、Cookie jar 和代理必须来自同一取证 baseline 或经过重新取证确认。
|
||
- cURL 中已有的 sign、token、Cookie challenge 值只能作为 fixture / 历史线索;最终项目必须通过补环境后的 signer 和同一 session 请求链生成或刷新。
|
||
- 冲突记录必须写入 `case/notes/sample-baseline-conflict.md`、`case/notes/final-request-validation.md` 和最终总结。
|
||
- 如果用户坚持沿用 cURL 浏览器族,则暂停最终请求验证,要求使用同浏览器族取证工具重新采样 baseline,或让用户明确接受风险;风险确认不得替代成功验证。
|
||
|
||
|
||
高强度检测中,TLS 指纹兼容不是单独开关,而是要与浏览器取证 baseline 一起看。最终请求前必须核对:
|
||
|
||
- TLS / JA3 / JA4、ALPN、HTTP/2 / HTTP/3 能力与所选 impersonate 浏览器族一致。
|
||
- `User-Agent` 与 `navigator.userAgent` 一致。
|
||
- `sec-ch-ua`、`sec-ch-ua-platform`、`sec-ch-ua-mobile` 与 `navigator.userAgentData` 一致;取证浏览器是 Firefox 时不要伪造 Chrome Client Hints。
|
||
- `Accept-Language` 与 `navigator.language/languages` 一致。
|
||
- `Accept-Encoding`、Header 顺序、HTTP/2 pseudo-header 顺序、`sec-fetch-*`、`Referer`、`Origin` 与 HAR / 浏览器请求链一致。
|
||
- 代理 / IP / 地区、timezone、locale、WebRTC 策略与 fingerprint baseline 一致。
|
||
- Cookie jar 和 Storage 状态来自同一 session 链路,不复制旧 cURL 中已过期的风控状态。
|
||
|
||
如果以上任一项冲突,先修正请求链和 fixture,不要把失败直接归因于 JS 补环境。
|
||
|
||
## Firefox baseline 与 curl_cffi TLS 指纹对齐
|
||
|
||
当取证浏览器 baseline 是 Firefox(例如 ruyiPage 定制 Firefox、用户手动 Firefox 或其他 Firefox 系取证工具),而最终请求客户端选择 Python `curl_cffi` / Node.js `curl-cffi-node` 时,不得仅凭 `impersonate="firefox147"`、`impersonate="firefox"` 或修改 `User-Agent` 就宣称与取证浏览器一致。Firefox 版本标签只是 curl_cffi 支持的预设 profile 名称,真实是否兼容必须以当前 case 的浏览器 TLS / HTTP2 采样证据为准。
|
||
|
||
### 硬性流程
|
||
|
||
1. 先按 `fingerprint-baseline-consistency.md` 固化当前 case 的 Firefox 指纹基线,记录 `baselineId`、取证工具、profile / seed、代理、语言、时区、UA、Header 和浏览器路径摘要。
|
||
2. 使用已确认的取证工具在同一 baseline 下访问 TLS 指纹检测端点,建议至少采样:
|
||
- `https://tls.peet.ws/api/all`
|
||
- `https://tls.browserleaks.com/json`
|
||
3. 记录真实 Firefox baseline 的 `ja3`、`ja3_hash`、`ja3n_hash`、`ja4`、cipher suites、extension 顺序、supported groups / curves、signature algorithms、delegated credentials、record size limit、key_share、ALPN、HTTP/2 Akamai fingerprint、UA 和请求头。
|
||
4. 再用 curl_cffi 的候选 profile(例如 `firefox147` 或当前 `firefox` alias)访问同一端点并对比。
|
||
5. 如果裸 profile 与 Firefox baseline 不一致,不允许只改 UA 后继续;必须尝试通过 `ja3`、`akamai`、`extra_fp`、`curl_options` 和 HTTP Header 对齐。
|
||
6. 对齐后再次访问检测端点,只有 `ja3_hash`、`ja3n_hash`、`ja4`、HTTP/2 Akamai fingerprint、关键 TLS 字段和 UA / Header 与 baseline 一致或经过用户确认可接受等价时,才允许进入最终请求验证。
|
||
7. 对齐配置必须写入 `case/notes/final-request-validation.md` 和最终总结;如果仍不一致,必须暂停、换客户端或记录用户明确接受的风险,不得伪造成功。
|
||
|
||
### 必须对比的字段
|
||
|
||
- TLS:`ja3`、`ja3_hash`、`ja3n_hash`、`ja4`、TLS version、cipher suites 列表和顺序、TLS extensions 列表和顺序、supported groups / curves、signature algorithms、delegated credentials、record size limit、key_share 数量和顺序、ALPN、ECH / certificate compression 等扩展摘要。
|
||
- HTTP/2:Akamai fingerprint、settings、window update、streams、pseudo-header 顺序。
|
||
- HTTP Header:`User-Agent`、`Accept`、`Accept-Language`、`Accept-Encoding`、`Referer`、`Origin`、`Sec-Fetch-*`、Header 顺序和大小写。
|
||
- Session 与环境:Cookie jar、代理 / IP、timezone、locale、`navigator.userAgent`、`navigator.language/languages`、Firefox 不应伪造 Chrome UA-CH。
|
||
|
||
### curl_cffi Firefox 对齐模板
|
||
|
||
以下模板用于“Firefox baseline 已采样,且确认 curl_cffi 裸 profile 不一致”时的对齐。示例中的具体值来自某次 ruyiPage Firefox 151 baseline 验证,只能作为写法参考;真实 case 必须把 `FIREFOX_BASELINE_JA3`、`FIREFOX_BASELINE_AKAMAI`、`headers`、`extra_fp` 和 `curl_options` 替换为当前 case 采样结果。
|
||
|
||
```python
|
||
from curl_cffi import requests
|
||
from curl_cffi.const import CurlOpt
|
||
|
||
FIREFOX_BASELINE_JA3 = (
|
||
"771,4865-4867-4866-49195-49199-52393-52392-49196-49200-"
|
||
"49171-49172-156-157-47-53,"
|
||
"0-23-65281-10-11-35-16-5-34-18-51-43-13-45-28-27-65037,"
|
||
"4588-29-23-24-25-256-257,0"
|
||
)
|
||
|
||
FIREFOX_BASELINE_AKAMAI = "1:65536;2:0;4:131072;5:16384|12517377|0|m,p,a,s"
|
||
|
||
FIREFOX_HEADERS = {
|
||
# 中文说明:UA 必须来自当前 case 的 Firefox baseline,不要沿用 curl_cffi 默认 UA。
|
||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:151.0) Gecko/20100101 Firefox/151.0",
|
||
# 中文说明:Accept / Accept-Language / Accept-Encoding 等也要以 HAR 或浏览器样本为准。
|
||
}
|
||
|
||
FIREFOX_EXTRA_FP = {
|
||
# 中文说明:signature_algorithms 必须按真实 Firefox baseline 的顺序填写。
|
||
"tls_signature_algorithms": [
|
||
"ecdsa_secp256r1_sha256",
|
||
"ecdsa_secp384r1_sha384",
|
||
"ecdsa_secp521r1_sha512",
|
||
"rsa_pss_rsae_sha256",
|
||
"rsa_pss_rsae_sha384",
|
||
"rsa_pss_rsae_sha512",
|
||
"rsa_pkcs1_sha256",
|
||
"rsa_pkcs1_sha384",
|
||
"rsa_pkcs1_sha512",
|
||
"rsa_pkcs1_sha1",
|
||
],
|
||
# 中文说明:delegated credentials 使用冒号分隔;不要把逗号分隔误写为最终配置。
|
||
"tls_delegated_credential": "ecdsa_secp256r1_sha256:ecdsa_secp384r1_sha384:ecdsa_secp521r1_sha512",
|
||
# 中文说明:record_size_limit 需来自真实浏览器样本,例如 Firefox 151 样本为 0x4001。
|
||
"tls_record_size_limit": 0x4001,
|
||
}
|
||
|
||
|
||
def create_firefox_aligned_curl_cffi_session():
|
||
# 中文说明:最终项目必须使用 Session;动态资源刷新、Cookie 生成和目标 API 都复用同一 Session。
|
||
# 中文说明:ja3 / akamai / extra_fp / curl_options 在 Session 初始化时固定,避免单次请求遗漏。
|
||
session = requests.Session(
|
||
impersonate="firefox147",
|
||
ja3=FIREFOX_BASELINE_JA3,
|
||
akamai=FIREFOX_BASELINE_AKAMAI,
|
||
extra_fp=FIREFOX_EXTRA_FP,
|
||
curl_options={
|
||
# 中文说明:用于对齐 Firefox key_share 数量;实际值必须通过采样验证。
|
||
CurlOpt.TLS_KEY_SHARES_LIMIT: 2,
|
||
},
|
||
)
|
||
session.headers.update(FIREFOX_HEADERS)
|
||
return session
|
||
|
||
|
||
def send_with_firefox_aligned_tls(session, method, url, **kwargs):
|
||
headers = dict(FIREFOX_HEADERS)
|
||
headers.update(kwargs.pop("headers", {}) or {})
|
||
return session.request(
|
||
method=method,
|
||
url=url,
|
||
headers=headers,
|
||
**kwargs,
|
||
)
|
||
```
|
||
|
||
### 对齐验收示例
|
||
|
||
对齐成功后,检测端点应至少达到以下级别:
|
||
|
||
```text
|
||
ja3_hash: 与 Firefox baseline 一致
|
||
ja3n_hash: 与 Firefox baseline 一致
|
||
ja4: 与 Firefox baseline 一致
|
||
akamai_hash / akamai_text: 与 Firefox baseline 一致
|
||
HTTP User-Agent: 与 navigator.userAgent / HAR 一致
|
||
HTTP/2: settings、window update、pseudo-header 顺序一致
|
||
```
|
||
|
||
若只修改 UA 后 `ja3_hash`、`ja4`、Akamai HTTP/2 指纹仍不一致,视为未对齐,不得进入最终请求验证。
|
||
|
||
### cyCronet / cycronet 的边界
|
||
|
||
`cyCronet` / `cycronet` 更适合 Chromium / Chrome baseline。即使当前版本提供 `add_tls_profile` / `set_tls_profiles`,通常也主要控制 cipher suites、curves 和部分 extensions,底层仍是 Cronet / Chromium 网络栈,HTTP/2 Akamai 指纹、JA4 结构和 Firefox 的 BoringSSL / NSS 行为可能不同。
|
||
|
||
- 取证 baseline 是 Firefox:优先使用 curl_cffi 的 Firefox 对齐流程;不要把 cyCronet 作为 Firefox 对齐首选。
|
||
- 取证 baseline 是 Chrome / Chromium / Camoufox Chromium 类工具:可以考虑 cyCronet,并同样先采样 baseline、再对比 JA3 / JA4 / HTTP2 指纹。
|
||
- 如果用户明确要求用 cyCronet 对齐 Firefox,必须先实测;若 JA4 或 HTTP/2 指纹无法一致,暂停并说明能力边界。
|
||
|
||
## Session 模式硬规则
|
||
|
||
最终请求不再区分单请求或请求链。只要用户选择发送真实请求或交付可请求的 `final.js` / `final.py`,必须读取 `session-request-chain.md` 并满足:
|
||
|
||
1. 创建 session client,不使用无状态单次请求。
|
||
2. 动态资源刷新、Cookie / challenge 生成、加密参数生成前后请求和目标 API 都走同一 session。
|
||
3. 同一 session 复用 Cookie jar、UA、Client Hints、Accept-Language、Referer、Origin、Header 顺序、代理、TLS 指纹和 fingerprint baseline。
|
||
4. 成功、失败或异常退出后,在 `finally` 中调用 `close()` / `exit()` / `dispose()`,并清理 Cookie jar、敏感 header、token 和临时响应。
|
||
5. 最终总结记录 session client 类型、请求链、Cookie jar 来源、销毁方式和敏感状态清理结果。
|
||
|
||
## Node.js CycleTLS 模板
|
||
|
||
CycleTLS 的包名和导出在不同版本中可能不同,先用 `check_tls_clients.js` 确认。最终项目中建议封装成 `result/src/request/client.js`,只由 `result/final.js` 调用。
|
||
|
||
```javascript
|
||
'use strict';
|
||
|
||
async function createCycleTLSSession({ ja3, userAgent, headers = {} }) {
|
||
const initCycleTLS = require('cycletls');
|
||
const cycleTLS = await initCycleTLS();
|
||
const cookieJar = new Map();
|
||
|
||
async function request({ url, method = 'GET', headers: extraHeaders = {}, body }) {
|
||
const response = await cycleTLS(url, {
|
||
method,
|
||
headers: { ...headers, ...extraHeaders },
|
||
body,
|
||
ja3,
|
||
userAgent,
|
||
}, method.toUpperCase());
|
||
// 中文说明:这里按项目需要解析 Set-Cookie 并写回 cookieJar,后续请求复用。
|
||
return { status: response.status, headers: response.headers, body: response.body };
|
||
}
|
||
|
||
async function close() {
|
||
cookieJar.clear();
|
||
if (typeof cycleTLS.exit === 'function') cycleTLS.exit();
|
||
}
|
||
|
||
return { request, close, cookieJar };
|
||
}
|
||
|
||
module.exports = { createCycleTLSSession };
|
||
```
|
||
|
||
## Node.js impers 模板
|
||
|
||
`impers` 是 Node.js curl-impersonate 路线,官方说明它处于 alpha 状态,API 可能变化。使用时优先按本机安装版本确认导出。常见用法是指定 `impersonate`,例如 `chrome`。
|
||
|
||
```javascript
|
||
// ESM 示例;若最终项目使用 CommonJS,可把请求客户端单独写成 .mjs 或动态 import。
|
||
import * as impers from 'impers';
|
||
|
||
export async function sendRequestWithImpers({ url, method = 'GET', headers = {}, body, impersonate = 'chrome' }) {
|
||
const fn = method.toLowerCase();
|
||
if (typeof impers[fn] === 'function' && !body) {
|
||
return await impers[fn](url, { headers, impersonate });
|
||
}
|
||
return await impers.request({ url, method, headers, body, impersonate });
|
||
}
|
||
```
|
||
|
||
## Python curl_cffi 模板
|
||
|
||
普通 Chrome / Safari / 已有预设 profile 可使用下列基础模板;如果取证 baseline 是 Firefox 且 curl_cffi 预设 profile 与真实浏览器 TLS / HTTP2 指纹不一致,必须优先按上文“Firefox baseline 与 curl_cffi TLS 指纹对齐”生成专用 Session 封装,不得只使用基础模板或只改 UA。
|
||
|
||
```python
|
||
from curl_cffi import requests
|
||
|
||
|
||
def create_curl_cffi_session(headers=None, impersonate="chrome"):
|
||
session = requests.Session(impersonate=impersonate)
|
||
session.headers.update(headers or {})
|
||
return session
|
||
|
||
|
||
def send_request_with_curl_cffi(session, url, method="GET", data=None, json_data=None, headers=None):
|
||
resp = session.request(
|
||
method=method,
|
||
url=url,
|
||
headers=headers or {},
|
||
data=data,
|
||
json=json_data,
|
||
timeout=30,
|
||
)
|
||
return {"status": resp.status_code, "headers": dict(resp.headers), "text": resp.text}
|
||
|
||
|
||
def close_curl_cffi_session(session):
|
||
try:
|
||
session.cookies.clear()
|
||
finally:
|
||
session.close()
|
||
```
|
||
|
||
## Python cffi_curl / cyCronet 模板
|
||
|
||
`cffi_curl` 与 `cyCronet` / `cycronet` 的 API 版本差异较大,Skill 不应硬编码某个本机版本。`cyCronet` / `cycronet` 默认优先用于 Chrome / Chromium baseline;Firefox baseline 应优先走 curl_cffi 对齐流程,除非已实测 cyCronet 的 JA3 / JA4 / HTTP2 与 Firefox baseline 一致。使用方式:
|
||
|
||
1. 先运行 `check_tls_clients.js --python python --json` 确认导入名。
|
||
2. 在 `result/src/request_client.py` 封装一个统一函数 `send_request(...)`。
|
||
3. 只在封装内部处理库差异;`final.py` 不直接散落库调用。
|
||
4. 若当前包 API 与模板不一致,以本机包文档为准,并在 `notes/final-request-validation.md` 记录使用版本和参数。
|
||
|
||
伪代码:
|
||
|
||
```python
|
||
def send_request(url, method="GET", headers=None, body=None):
|
||
# 这里根据用户已确认且本机已安装的 cffi_curl / cyCronet API 实现。
|
||
# 必须返回 status、headers、text/body。
|
||
raise NotImplementedError("请根据本机 cffi_curl / cyCronet 版本补齐请求客户端封装")
|
||
```
|
||
|
||
## 最终验证流程
|
||
|
||
1. 前置阶段确认最终请求客户端和是否已安装。
|
||
2. 确认 fixtures 已通过,Node.js 生成参数与浏览器样本一致。
|
||
3. 若目标 JS 依赖 XHR/fetch 网络访问,先按 `xhr-fetch-session-bridge.md` 建立 live session bridge,并确认 Cookie jar / Set-Cookie / resource timing 同步。
|
||
4. 读取用户提供的成功 cURL / HAR,不凭空构造请求。
|
||
5. 脱敏保存请求样本;真实 Cookie / token 只保存在本地,不写入最终报告。
|
||
6. 使用已确认的 CycleTLS / impers / curl-cffi-node / curl_cffi / cffi_curl / cyCronet 创建 session client,并在同一 session 中仅发起少量验证请求;若用户选择“不发真实请求”,则只输出本地 sign / 参数。
|
||
7. 对比:
|
||
- HTTP 状态码。
|
||
- 响应 JSON 中关键字段。
|
||
- 服务端是否接受新生成的加密参数。
|
||
- 是否出现风控 / 验证码 / 登录失效。
|
||
8. 写入 `notes/final-request-validation.md`。
|
||
9. 在 `finally` 中销毁 session,清理 Cookie jar、临时响应、日志和敏感请求副本。
|
||
10. 将最终请求逻辑整合进 `result/final.js` 或 `result/final.py`,并运行 `check_xhr_fetch_session_bridge.js` 与 `check_final_artifact.js` 确认不包含浏览器自动化代码和多余产物。
|
||
|
||
## 输出模板
|
||
|
||
```markdown
|
||
## 最终请求验证
|
||
|
||
- 是否执行真实请求:是 / 否
|
||
- 用户授权:是 / 否
|
||
- 前置阶段已选客户端:Node.js CycleTLS / Node.js impers / Node.js curl-cffi / curl-cffi-node / Python curl_cffi / Python cffi_curl / Python cyCronet / 不发真实请求
|
||
- 客户端安装状态:已安装 / 未安装改选 / 未安装不发请求
|
||
- Session 模式:已创建 session / 不发真实请求
|
||
- 请求链是否复用同一 session:是 / 否,原因:
|
||
- XHR/fetch Session Bridge:未涉及 / offline-fixture / live-session-bridge;如使用 Python curl_cffi,是否由 final.py 持有同一 session 并服务 Node IPC:
|
||
- Session 销毁方式:close / exit / dispose / Cookie jar 清理 / 不适用
|
||
- 最终项目入口:final.js / final.py
|
||
- 是否包含浏览器自动化代码:否
|
||
- 请求来源:cURL / HAR / 用户样本
|
||
- TLS 指纹兼容原因:用户前置选择 / 目标接口要求浏览器网络栈一致 / 未启用
|
||
- Firefox / curl_cffi 对齐状态:不涉及 / 已对齐 / 未对齐改选 / 用户确认风险
|
||
- TLS baseline 来源:ruyiPage / Camoufox / CloakBrowser / 用户手动浏览器 / HAR;baselineId:
|
||
- 用户 cURL 浏览器族与取证 baseline 浏览器族:一致 / 冲突 / 未知;冲突处理:以取证 baseline 为准 / 重新取证 / 切换工具 / 用户确认风险
|
||
- 是否阻止沿用冲突 cURL 字段:UA / Client Hints / Header / TLS profile / HTTP2 指纹 / Cookie / 动态参数
|
||
- 对齐参数摘要:ja3 hash / ja4 / akamai hash / extra_fp / curl_options / Header 是否与 baseline 一致
|
||
- 请求次数:
|
||
- 状态码:
|
||
- 目标加密参数是否被接受:
|
||
- 响应关键字段:
|
||
- 是否触发验证码 / 风控:
|
||
- 敏感材料处理:已脱敏 / 仅本地保存 / 已清理
|
||
```
|
||
|
||
## 排查提示
|
||
|
||
如果 TLS 指纹兼容客户端仍失败,优先排查:
|
||
|
||
- Cookie / Authorization 是否过期。
|
||
- Header 顺序、大小写、HTTP/2 伪头、Content-Length 是否与样本一致。
|
||
- Body 是否保持原始字符串,未被 JSON 重新序列化。
|
||
- Query 编码、排序、空值、数组序列化是否一致。
|
||
- 时间戳、nonce、随机数、server seed 是否过期。
|
||
- IP / 代理 / 地域 / 账号风控是否变化。
|
||
- 目标接口是否绑定浏览器会话、Service Worker、一次性 token 或挑战结果。
|