Files
live-hub-py/docs/HUYA_COOKIE合并审计与来源矩阵.md
T

11 KiB
Raw Blame History

虎牙网页 Cookie 合并审计与来源矩阵

更新时间:2026-09-01

结论

core/huya/app_login.py::_merge_web_cookie 只应合并当前登录流程实际得到的认证值和已有网页 Cookie。Cookie 在抓包中出现,不代表可以凭长度或字符集在本地生成。

9.1 抓包中,活动 WSS 的角色查询和积分查询均成功,说明活动接口当前主要依赖核心认证字段;这不能证明随机设备字段、商城接口和支付链路会接受伪造值。

字段来源

字段 真实来源 当前策略
udb_cred App WUP 登录响应的 114B credBase64URL 后 152 字符 本次值覆盖旧值
udb_uidyyuid WUP 登录响应解析出的真实 UID 本次值覆盖旧值
udb_biztoken tryQrLogin(stage=2) 响应 本次值覆盖旧值
udb_passportusername 网页 Cookie/tryQrLogin/App 登录态中的真实 passport 只有输入本身是 hy_ 账号时才写入,否则保留已有值
udb_versionudb_originudb_status 网页登录协议固定字段 写入协议固定值
sdidhdid df/collectSet-Cookie/响应 本次指纹响应覆盖旧值
guid 网页/launch 会话的 GUID32 只接受显式 device_info["guid"] 或 Session 值
udb_guiddata 浏览器设备状态字段,和 guid 是不同值 只接受显式值,不再与 guid32 复制
game_did__yamid_*、QIMEI、udb_anobiztoken 浏览器/设备 SDK 状态,部分具有关联或签名 无真实值时省略
udb_deviceid 浏览器设备 Cookie 只接受格式正确且显式提供的值
Hm_*HMACCOUNT*_rep_cnt_yasids 页面统计与访问计数状态 只保留已有或显式提供值

9.1 抓包事实

同一网页会话内这些值保持稳定:

  • _qimei_uuid4242 位;_qimei_h3838 位;_qimei_fingerprint:32 位。三者是 QIMEI 设备标识,不能用随机十六进制替代。
  • udb_anobiztoken344 位 URL-safe 字符串,和 udb_anouid 共同属于匿名设备状态,不能用重复 UUID 字节拼接。
  • guidudb_guiddata 均为 32 位,但抓包值不同,不能复用同一画像字段。
  • udb_deviceidw_ 加 19 位数字;格式可以校验,但仅凭格式无法得到真实值。
  • huya_flash_rep_cnthuyasp_rep_cnthuya_hd_rep_cnthuya_web_rep_cnt 随访问过程变化,不能硬编码抓包历史值。

抓包中还存在 huya_ua,它是 WSS/HTTP RPC 的业务 UserId 字段,不属于持久化 Cookie;由 wss_client.pyhttp_client.py 按通道注入。

旧实现的问题

旧实现曾在空 Session 中生成 __yamid_newgame_did、三个 QIMEI 字段、udb_anobiztokenHMACCOUNT_yasids 和多个固定计数器。它们只能满足长度测试,不能证明值具有协议语义。

旧实现还把 device_info["guid32"][:32] 同时写入 guidudb_guiddata,并把手机号直接写入 udb_passport/username。这两点会造成设备身份和账号映射偏差。

验证要求

  1. 登录后必须检查 udb_credudb_biztoken、UID、sdidhdid 的来源和值覆盖关系。
  2. 活动 WSS、商城 WSS、下单和支付分别验证,不能用角色查询成功替代全流程验证。
  3. 新增 Cookie 字段前,必须有同一会话的响应、页面状态或设备 SDK 输出作为来源证据;只有长度/正则证据时保持省略。

9.1 抓包新增证据

705-meta.jsonGET www.huya.com/30596253 在请求发出时已经携带 43 个网页 Cookie, 包括 guidgame_did、三个 QIMEI、udb_guiddataudb_deviceidudb_anobiztoken 和统计字段;该响应没有 Set-Cookie。随后 714-meta.jsonPOST df.huya.com/web/df/collect 只返回 sdidhdid 两个 Set-Cookie

因此 App 的 /web/cookie/verify 不能兑换出完整网页 CK。网页设备字段来自浏览器此前 持久化的 Cookie/Storage 和页面脚本运行态;当前 Python App 登录链不应声称能凭空补齐它们, 也不在脚本中外挂浏览器。缺失时返回 COOKIE_INCOMPLETE,精英宝典任务不再回退 HTTP。

运行时复核(2026-09-01

当前 Node 指纹运行器已接入 __COOKIES__ 输出桥:只有 hydevice 通过 document.cookie 实际写入的值才会进入 HuyaSdidResult.cookies,并在合并阶段按 显式值转发。对当前内置 hydevice-prod-1.2.45-min.js 的实际运行结果为:

  • __SDID____HDID__ 正常返回;
  • Cookie 桥只观察到脚本自身的 udb_appid/临时 sdid,没有生成 guid、QIMEI、 udb_guiddataudb_deviceidudb_anobiztoken
  • 因此这次改动不会改变 COOKIE_INCOMPLETE 判定,也不会用随机值补齐字段。

从线上下载的 hydevice-prod-1.2.51-min.js 在现有 Node 仿真环境中还依赖未实现的 浏览器 API(首个错误为 window[t(...)] is not a function),没有将其替换进生产链。 后续若升级仿真环境,必须先观察到真实 Cookie 写入,再纳入合并逻辑。

多抓包交叉核验

  • 8.31/reqable完整抓包-1.har:首次页面请求和 QR 请求均已携带全部 7 个字段; QR 成功响应只新增 udb_credudb_uidyyuidudb_passportusernameudb_versionudb_biztokenudb_originudb_status
  • 8.31/cahrles完整抓包-1.har:第一个带网页设备态的请求早于 df/collect 和 QR 成功响应;df/collect 只下发 sdidhdid
  • 7.5-短信登录-滑块.harweb/cookie/verify 前后的请求也没有新增这 7 个字段。

因此当前日志中的缺失列表是协议事实,不是 Cookie 合并阶段丢失;下一步只有取得 同一网页会话的真实持久化设备态,或找到服务端正式签发接口,才能继续补齐。

真实浏览器实测(2026-09-01,未登录)

用真实 Chrome(有头)通过 CDP 抓包复核,两轮验证: core/huya/browser_harvest.pyround1 全新 profile / round2 同 profile 重启), 原始数据与报告在 evidence/browser_cookie_harvest/

页面序列:www.huya.com 门户 → 直播间 30596253,全程不登录。

字段 首次出现位置 产生机制 round2 重启后首请求携带
__yamid_new liveapi.huya.com/ip/getIpLocation (XHR) 门户页脚本写 document.cookie
game_did 同上 脚本写入
udb_guiddata udblgn.huya.com/web/middle/2.4/... (Document) UDB SDK 脚本写入,与 guid 值不同
guid udbres.huya.com/js/HyUDBWebSDK-Exchange-2.4.js UDB SDK 脚本写入
_qimei_uuid42 同上 QIMEI SDK 脚本写入
udb_deviceid udblgn.huya.com/web/middle/2.4/.../web/anonymousLoginSet-Cookie 服务端匿名接口下发,w_+19 位数字,10 年
udb_anobiztoken udblog.huya.com/web/log/report UDB SDK 脚本写入

结论:

  1. 7 个必填字段全部由浏览器页面运行态产生(6 个脚本写 document.cookie1 个 udb_deviceid 由匿名服务端接口下发),与登录协议无关 —— COOKIE_INCOMPLETE 判定是协议事实。
  2. round2 重启后门户首 Document 请求直接携带 16 个 Cookie(7 个必填字段全部在内), 证明这些字段是浏览器持久态,跨会话稳定复用。
  3. 首次访问时序:门户首请求无 Cookie → 脚本热启动后 __yamid_new/game_did 先出现 → UDB SDK 加载后 guid/udb_guiddata/QIMEI 出现 → anonymousLogin 匿名 XHR 携全套字段并把 udb_deviceid 种回 10 年。
  4. 全会话服务端 Set-CookiePHPSESSIDudb_deviceidsdidHMACCOUNT (百度统计)、mediav v 五类,其余全部来自脚本。

新突破口:udblgn.huya.com/web/anonymousLogin/web/middle/2.4/... 是公开 匿名接口(纯 HTTP 可调),服务端正式签发 udb_deviceid —— 这是“服务端正式签发 接口”的第一个实例;guid/QIMEI/udb_anobiztoken 仍无服务端下发,只能靠复刻 页面 SDK(HyUDBWebSDK-Exchange-2.4.js 等)的脚本语义。

匿名流程解密(2026-09-01,纯 HTTP 复放验证)

浏览器实际匿名链(未登录):

  1. GET udblgn.huya.com/web/middle/2.4/<8位随机>/https/<32hex>Set-Cookie: udb_deviceid=w_<19位>; Max-Age=315360000; Domain=huya.com 响应是一个加载 HyUDBWebSDK-Exchange-2.4.jsinit() 的引导 HTML。
  2. SDK 随后发 POST udblgn.huya.com/web/anonymousLogin Content-Type: application/jsonReferer 必须为 middle URL 否则返回 error!):
    • 请求体 {"uri":"10013","version":"2.4","context":"WB-<hex32>-<hex32>-<hex32>", "appId":5002,"sdid":"csid_<hex32>","lcid":2052,"byPass":3, "requestId":<8位>,"authId":"","data":{"domainList":""}}
    • 响应 returnCode:0data.uid12 位匿名 uid)、data.biztoken344 位 URL-safe)、Set-Cookie: udb_anouid7 天)+ udb_deviceid
  3. 页面 SDK 把 biztoken 写入 udb_anobiztokenuid 写入 udb_anouid 与 middle URL 的 32hex= udb_guiddata)、guid、QIMEI、__yamid_newgame_did 一起构成网页设备态。

对照验证(真实浏览器最终 Cookie 与响应值一一对应):

Cookie 值形态 来源
udb_deviceid w_+19 位数字 middle/anonymousLogin Set-Cookie10 年)
udb_anobiztoken 344 位,AQA… 开头 anonymousLogin 响应 data.biztoken
udb_anouid 12 位数字 anonymousLogin 响应 data.uid/Set-Cookie7 天)
udb_guiddata 32hex middle URL 的第三段参数(客户端自选)
guid 32hex 页面 SDK 脚本写入 document.cookie
_qimei_uuid42 42 位 QIMEI SDK 脚本写入
__yamid_new 32hex 门户业务脚本写入
game_did 35 位 门户业务脚本写入

复放实验结论(2026-09-01):

  • middle 的 8 位随机数与 32hex 均不校验:任意随机值 GET 即签发新的 udb_deviceidreturnCode 200);
  • anonymousLogin 的 context/csid/requestId 均为全新随机值时,无任何 Cookie 直接 POST 也返回 returnCode:0 + 新 biztokenSet-Cookie
  • 因此 udb_deviceidudb_anobiztokenudb_anouidudb_guiddata 四个字段可由纯协议生成,不再依赖真实浏览器脚本;
  • 剩余 guid_qimei_uuid42__yamid_newgame_did 没有服务端签发, 服务端是否校验其“真实性/关联性”未知 —— 200 只代表接口接受,不代表 风控认可。必须用协议生成的设备态跑通线上 活动 WSS/商城/支付 全链路 才能作为最终结论,不能只凭长度测试(与本文开头结论一致)。