Files
UHD_Media_Tool/docs/design/cloudflare-handling.md

5.0 KiB
Raw Permalink Blame History

CF-1 Cloudflare 验证处理(调研)

状态:CF-2 已按本文 §3 推荐方案 A→B 级联落地,另加出口统一(站点请求明确直连 / 站点代理)。运维与字段见 docs/runbook/sites.md「出口」「Cloudflare(CF-2)」、docs/runbook/browser.md §13。与本文的差异:同步 cf_clearance 单独存 site_account.user_info.cloudflare(值在 secret),普通浏览器回写仍剔除;视觉子流程最多 2 次;site.config.cloudflare 多了 lastAttemptAt / lastSolvedAt / pending / sessionId / lastResult / lastRule / lastError。

1. 复现(2026-09-30,HTTP 5 次、浏览器 5 次,间隔 ≥15s)

请求 结果
HTTP(Deno,Firefox UA)观众 / 憨憨搜索页,无 Cookie、仅 cf_clearance、补齐浏览器请求头 全部 302 → login.php(server: cloudflare,无 cf-mitigated):没被挑战
浏览器,观众账号 profile(已有 cf_clearance)搜索页 200,直接出结果
浏览器,全新 profile:憨憨 无挑战、无 CF Cookie
浏览器,全新 profile:观众 ×2 首次导航 403(挑战),1–5 秒内不点即过,拿到 cf_clearance(Cookie 期限 1 年;真实有效期由站点 Challenge Passage 决定,不可见)

结论:观众有 CF 挑战(非交互 managed/JS 类,挑战页类型未抓到),固定 Windows 身份的 camofox 能自动通过;憨憨未见。

关键发现:两端出口不一致。cdn-cgi/trace:浏览器(站点未开代理 → 直连)出口 HKG;app 的 HTTP 端走 globalThis.fetch,而 Deno 会读容器的 HTTPS_PROXY(override 里的 mihomo 中继)→ 出口 DFW。BR-4 §11.5 的"按站点统一出口"在 HTTP 侧没生效;代理换节点也是"随机出 CF"的可能来源。

分类缺陷:HTTP 遇挑战(403)在 spider fetchPage 被归为 auth_invalid → 账号标失效、开"登录失效"事项、触发重登;503 挑战被归为维护。

2. 试验:浏览器 cf_clearance + 同 UA 给 HTTP

HTTP 本身未被挑战,带不带 cf_clearance 都是 302,本次无法判定。另因出口不同(HKG vs DFW),即使被挑战也不满足"同 IP"前提。MP 的 HTTP 端是 requests/httpx(非浏览器 TLS),照样复用浏览器取得的 cookie+UA,说明多数 PT 站实际只绑 UA+IP。把"首次复用重试"本身作为在线验证,按站点记结果。

3. 方案对比

成功率 请求成本 礼貌 改动 与 BR-4
A 同步 cf_clearance+UA,HTTP 重试 1 次 取决于站点是否绑 TLS 最低 好 小 需改"仅浏览器"规则
B 该请求改由浏览器执行 + N 小时走浏览器标记 高 中(整页加载) 好 中 一致
C 站点长期全走浏览器 高 高 一般 中 一致
D FlareSolverr 与 camofox 相当 多一容器 — 大 不需要(用户否决)

推荐 A→B 级联(照 MP,camofox 即仿真浏览器):

  1. 触发:http.ts 识别 cf-mitigated: challenge 或 403/503 + underChallenge(html) → 新错误 SiteError("challenge");不再算凭据错误。
  2. 过挑战:用该账号 profile 开浏览器会话打开同一 URL。① 等自动通过(≤15s,轮询标题 / cf_clearance);② 未过 → browser.solve_challenge 视觉子流程,只允许"我是人类"复选框 / Turnstile 单步点击,maxSteps 小、最多 1–2 次;③ 图片选择 / 输码类 → 会话转 waiting(challenge_unsolved),生成待处理事项"观众需要你在原站视图里过一次验证",用户在桌面(同一会话)过完,交还即继续。约束:只处理用户已登录账号的站点;同站单次触发只处理一次,30 分钟节流;不接打码服务。
  3. 同步:读回 cf_clearance + navigator.userAgent,前提是账号 UA 来源 = browser 且两端出口相同;HTTP 重试 1 次。
  4. 回退:仍被挑战 → 本次请求改为浏览器导航取 outerHTML 交给现有解析器;站点记 browserMode 6 小时(续期 / 到期再试 HTTP);记 cfReuse = fails,以后跳过第 3 步。
  5. 展示:站点页显示"Cloudflare:浏览器代理中,至 hh:mm / 需人工验证 / 复用有效"。

4. 实现清单

  • packages/adapters/src/sites/{http.ts,types.ts,spider/adapter.ts}:challenge 错误类,修 403 误判。
  • sources-identity/runtime.ts:非代理站点显式 Deno.createHttpClient({proxy:null}) 直连(或浏览器也按 env 代理),两端同出口;siteFetch 挂挑战钩子(重试 / 浏览器执行)。
  • service/credentials.ts、browser/cookies.ts:splitBrowserOnlyCookies 改为"同 UA 同出口时 cf_clearance 进 HTTP Cookie",__cf_bm 仍不带。
  • site.config.cloudflare = {browserUntil, cfReuse, lastChallengeAt, lastSolvedBy};snapshot.ts 不再对 challenge 调 onAuthError。
  • browser/service.ts:新增 fetchViaBrowser(siteId, url)(profile 锁、限流令牌、site_disabled 检查);enterWaiting / 待处理事项、桌面接管(WT-1)、BR-3 重连沿用。
  • agent/challenge.ts:无父 Agent 时由系统发起子流程(预算来自设置);Turnstile 在跨域 iframe,click_at 需插件提供真实鼠标坐标点击。
  • 测试:挑战分类、级联状态机、出口一致。