Shopee什么条件可以免登录,谈谈Shopee DFP / SFU 遥测了什么数据



不是一个 sz-token,而是 DFP 指纹报告 + 服务端 riskToken + SFU 头注入 + SAP 旁路签名
快速总结
- 这份 Shopee DFP 的主体是一份浏览器安全遥测报告,不是单个
sz-token。 - 当前样本实际上报
103个 TLV 遥测字段:环境/指纹/行为88个,API / 代码完整性探针15个。 - 它上报的内容包括:UA/UA-CH、platform/vendor、screen/viewport、CSS media query、media codec、network、time/perf、Canvas/Picasso、pixel sample、WebGPU/GPU、storage/memory、cookie 能力、设备/会话 ID、环境状态位、行为 packed report。
- API / 代码完整性遥测包括:Permissions API 状态、Storage Access 错误、structured clone 异常、webdriver/global 变量、fetch/XHR/cookie hook 痕迹、函数源码快照。
- 它的遥测方式是:浏览器 API 读值 + canvas/GPU 渲染采样 + API 行为/异常触发 + 函数源码/原型 hook 快照 + 行为队列 packed;这些值再进入 VMP 的 TLV/LV 字段协议。
- 它的上报方式是:
103个遥测字段 -> VMP TLV/LV -> gzip -> AES-CBC/PKCS7 -> RSA key envelope ->POST/v2/shpsec/web/report。 - 登录/校验页不是 DFP report 上报和
riskToken消费的必选前置条件;它更像本地环境、账号态、cookie、地域或风控状态不满足时出现的分支。 - 用到的编码/混淆/加密层包括:字段级字符替换、hash-like 摘要、packed numeric、TLV tag/length/value XOR、LV/RC4-like 字段扰动、gzip/deflate、base64、AES-CBC/PKCS7、RSA-1024/PKCS#1 v1.5 key envelope。
/report返回的data.riskToken会写入shopee_webUnique_ccd,随后进入业务请求头sz-token和af-ac-enc-sz-token。- VMP 协议目录里的 BODY
213个字段、PAYLOAD3个字段只是字段目录,不等于当前 report 实际上报了 216 个遥测值。
本轮重新扫了整个 /Volumes/haidragon-D/shopee,不是只看 token_analysis_20260703。高信号证据源按用途分成这几类:
|
|
|
|
|---|---|---|
|
|
dfp_trace_20260703/dfp_trace_chrome_headless_tlv1.decoded.json |
decodedFields 的 103 个字段。 |
|
|
dfp_trace_20260703/dfp_trace_chrome_headless_tlv1.originals.json |
tlv.fields[].value 是进入 TLV 创建函数的值。 |
|
|
dfp_trace_20260703/dfp_trace_chrome_headless_tlv1.jsonl |
sfu.tlv.call、 sfu.rc4.call、 sfu.rc4.ret,确认字段进入 VMP TLV/LV 路径。 |
|
|
token_analysis_20260703/vmp_static/program_analysis/report_tlv_decoded_pc16.json |
103 个 TLV 字段。 |
|
|
token_analysis_20260703/vmp_static/runtime_field_tables.json |
213 个 slot、report 字段、async/sync 分组和 PAYLOAD 3 个字段。 |
|
|
token_analysis_20260703/vmp_static/program_analysis/report_crypto_dynamic_pc16.json
report_frame_layout_pc14.json |
|
|
|
token_analysis_20260703/trace/dfp_trace_20260704_report_response_pc5/*.jsonl
pc6/*.jsonl |
/report 返回 token 后写缓存,并进入后续 header。 |
|
|
dfp_trace_20260703/dfp_trace_chrome_headless_deep_ce1_cem.jsonl
token_analysis_20260703/trace/dfp_trace_20260704_picasso_pc18..pc22/*.jsonl、 ce1_writer_pc25..ce1_h_raw_pc29/*.jsonl |
Ce1 不是 JSON/AES 密文,而是 Picasso/canvas payload。 |
|
|
token_analysis_20260703/vmp_static/program_analysis/*.md
decompiled_entries/*.md |
vmp_resolveAllFields、 vmp_createTLV、 vmp_createLV、crypto/helper 模块角色。 |
|
|
dfp_trace_chrome_headless_tlv1.decoded.md
dfp_trace_chrome_headless_tlv1.originals.md、 shopee_dfp_flow_analysis_20260703.md、 shopee_dfp_ce1_cem_deep_analysis_20260703.md、 token_analysis_20260703/analysis_report.md |
|
token_analysis_20260703/trace/codex_tmp_migrated_20260704_0758 下面还有同名迁移副本,用来保留当时的临时工作区产物;本文按 canonical 源文件去重,不把迁移副本重复计入字段数。
这条链路的重点不在于“某个 header 怎么拼”,而在于四件事:
- 这
103个 DFP TLV 字段分别上报了什么浏览器遥测 - API / 代码完整性探针怎样作为遥测证据上传给服务端
- 遥测报告怎样经过 TLV / LV、gzip、AES、RSA 和二进制 frame 变成
/reportbody /report返回的riskToken怎样进入缓存,并由 SFU wrapper 汇合到后续业务请求
按当前本地样本,这条 Shopee 请求链路更像下面这个结构:
浏览器指纹/行为/ API /代码完整性遥测->当前样本103个 TLV 上报字段-> VMP TLV / LV field frame-> gzip-> AES-CBC/PKCS7 + RSA-1024 key envelope-> POST /v2/shpsec/web/report->服务端返回 data.riskToken-> cookie/localStorage: shopee_webUnique_ccd->后续/api/v4/search/search_items 请求头
本文里的所有运行值都是脱敏演示值。真实 token、真实 hash、真实 AES key、真实请求 body、真实 cookie 值都没有写入。
一、先分清三条链路
Shopee 这份样本里容易混在一起的是 DFP、SFU 和 SAP。它们会在同一个业务请求上汇合,但角色不同。
|
|
|
|
|
|
|---|---|---|---|---|
|
|
|
/v2/shpsec/web/report |
REPORT_FRAME_B64_FAKE_PREFIX... |
|
|
|
/report
|
shopee_webUnique_ccd |
|
|
|
|
|
57846 |
af-ac-enc-dat:DAT_FAKE_16HEX |
|
|
|
|
x-sap-ri
x-sap-sec |
x-sap-sec:BASE64_FAKE_2164_CHARS... |
|
因此, sg.xiapibuy.com、 /verify/traffic 这类现象要按环境分支看,不能和 /report、 /api/v4/search/search_items 混成同一个“商品数据接口”。本地环境、账号态、cookie、地域状态或风控状态不对时,页面可能被强制带到登录/校验分支;如果本地环境过了,这条登录分支不是必经链路。
- 商品搜索接口是
/api/v4/search/search_items - DFP 上报接口是
/v2/shpsec/web/report sg.xiapibuy.com在当前语境里应理解为本地环境不达标时出现的登录/校验分支,不是商品搜索真实数据接口,也不是环境正常后的必经数据链路
二、SFU wrapper 到底注入了什么
SFU 外层模块 57846 负责把 DFP SDK 的结果合并到业务请求。这里的字段名是明文常量,不是猜出来的。
|
|
|
|
|
|---|---|---|---|
|
|
|
DFP_SHORT_FAKE_64BIT_STATE |
useHeader=false
getClientCheckData() 的结果进入请求数据字段。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
DAT_FAKE_16HEX |
|
|
|
|
1.12.40 |
|
|
|
|
|
|
这里最关键的修正是: getDegradedData() 在当前链路里不是“重新加密生成 af-ac-enc-sz-token”。后续静态和 pc6 runtime 交叉确认,它走的是:
getDegradedData()-> getShortToken()->当前内存 token slot->或 getCacheDfp()-> cookie/session/storage 中的 shopee_webUnique_ccd
也就是说, af-ac-enc-sz-token 在这次样本里就是 /report 返回 token 的缓存读出结果。
三、主 DFP report 上报了什么遥测
Shopee 这套 DFP report 的主题不是“生成一个 sz-token”,而是先把浏览器侧环境、能力、指纹、行为和完整性探针采集成一份遥测报告,再由 /v2/shpsec/web/report 送到服务端。服务端返回的 riskToken 只是这份遥测报告被消费后的结果之一。
当前样本里,真正落进 TLV 的上报遥测字段是 103 个。字段名保留 VMP 原始别名,值全部改成脱敏演示值;这里不把 SFU header、SAP header、BODY/PAYLOAD 协议目录混进遥测字段表。
这份 103 个字段可以按遥测主题理解为:
- Navigator / UA / UA-CH / platform / vendor
- 屏幕、窗口、viewport、CSS media query
- 媒体 codec、网络状态、时间戳、计数/性能指标
- Canvas / Picasso / pixel sample / 图形摘要
- WebGPU / GPU feature / adapter 信息
- storage quota、内存/存储额度、cookie 能力
- 会话/设备 ID、环境状态位、hash-like 子指纹摘要
- 行为/report packed payload
- API 行为、API 异常、权限状态、hook 检测、函数源码完整性
字段完整性按本地文件做过交叉核对:
|
|
|
|
|
|---|---|---|---|
dfp_trace_20260703/dfp_trace_chrome_headless_tlv1.decoded.json |
decodedFields[].key |
103 |
|
dfp_trace_20260703/dfp_trace_chrome_headless_tlv1.originals.json |
firstSearchPhase.tlv.fields[].keyCandidates[0] |
103 |
|
token_analysis_20260703/vmp_static/program_analysis/report_tlv_decoded_pc16.json |
decodedFields[].key |
103 |
|
token_analysis_20260703/vmp_static/program_analysis/report_tlv_originals_pc16.json |
firstSearchPhase.tlv.fields[].keyCandidates[0] |
103 |
|
runtime_field_tables.json 里的 BODY 213 个字段和 PAYLOAD 3 个字段是 VMP 协议目录:它说明“哪些字段可能被 VMP 管”,但不等于当前这次 report 实际上报了 216 个遥测值。所以正文遥测表只按 decoded/originals 里实际落进 TLV 的 103 个字段写。
1. 环境 / 指纹 / 行为遥测字段全表
这一表是当前 report 里已经确认会上报的常规遥测字段,共 88 个。它们不是业务商品数据,而是服务端用于交叉判断浏览器环境是否一致、是否像真实环境、行为队列是否自然的一组证据。
|
|
|
|
|
|---|---|---|---|
CEw |
媒体能力 |
ogg:probably;wav:probably;mp4a:probably;xMpegUrl:maybe |
|
CEb |
屏幕/窗口 |
1440;900 |
|
CEX |
图形指纹摘要 |
FAKE40HEX_CANVAS_AUDIO_DIGEST |
|
CEs |
预留/空值 |
"" |
|
CED |
环境状态位 |
37 |
|
CER |
预留/空值 |
"" |
|
CEt |
图形指纹摘要 |
FAKE40HEX_RENDER_DIGEST |
|
CEr |
网络状态 |
4g;10;50 |
|
Fis |
环境状态位 |
51 |
|
CEl |
预留/空值 |
"" |
|
CEx |
环境状态位 |
71 |
|
CEK |
环境状态位 |
119 |
|
CEW |
预留/空值 |
"" |
|
CEY |
语言/地区 |
en-SG |
|
CEm |
环境状态位 |
97 |
|
CEn |
环境状态位 |
120 |
|
CG1 |
环境状态位 |
65 |
|
CG4 |
环境状态位 |
1 |
|
CG9 |
Navigator/环境 |
undefined |
|
CGI |
预留/空值 |
"" |
|
CGd |
插件列表 |
ChromePDFViewer;PDFViewer;WebKitbuilt-inPDF |
|
FNW |
屏幕/窗口 |
1.00 |
|
CGo |
环境状态位 |
31 |
|
CGF |
屏幕/窗口 |
1440;900 |
|
CGE |
会话/设备ID |
00000000-1111-4222-8333-abcdef123456 |
|
FiD |
会话/设备ID |
00000000-1111-4222-8333-abcdef123456 |
|
CGf |
系统平台 |
IntelMacOS X14_4_1 |
|
CGw |
系统平台 |
Macintosh |
|
CGj |
Navigator/UA |
Mozilla/5.0(Macintosh;IntelMacOS X14_4_1)AppleWebKit/537.36(KHTML,likeGecko)Chrome/149.0.0.0Safari/537.36 |
|
Fiy |
语言/地区 |
ll_ss |
|
CGk |
Navigator/环境 |
GoogleInc.(Apple) |
|
CGh |
预留/空值 |
"" |
|
CGK |
预留/空值 |
"" |
|
CGY |
布局/字体测量 |
[9508,9508,9228,9509,9521,...] |
|
CGz |
环境摘要 |
FAKE_ENV_DIGEST_56CHARS |
|
CGm |
Canvas/Picasso |
[[255,20,147,255],[123,104,238,255],[32,178,170,255],...] |
|
CGL |
指纹摘要数组 |
["FAKE40HEX_A","FAKE40HEX_B","FAKE40HEX_C"] |
|
Ce1 |
Canvas/Picasso |
CE1_FAKE_PICASSO_DATA_URL_PAYLOAD |
|
Ce2 |
预留/空值 |
"" |
|
Ce3 |
预留/空值 |
"" |
|
CEp |
预留/空值 |
"" |
|
CEd |
预留/空值 |
"" |
|
Ce5 |
预留/空值 |
"" |
|
Ce6 |
WebGPU/GPU |
packed_4x8_integer_dot_product,subgroup_uniformity,shader-f16,... |
|
Ce7 |
WebGPU/GPU |
bgra8unorm |
|
Cea |
WebGPU/GPU |
1,1,1,1,1 |
|
FQv |
WebGPU/GPU |
0 |
|
FQp |
随机/浮点探针 |
0.6446436629 |
|
FQ8 |
Canvas/Pixel |
255255255255128128128255191191191255646464255 |
|
CeO |
环境状态位 |
94 |
|
Cej |
计数/性能 |
27542 |
|
Ceq |
计数/性能 |
19398 |
|
Cey |
环境状态位 |
126 |
|
CeX |
计数/性能 |
3437 |
|
CeZ |
环境状态位 |
29 |
|
CeT |
环境状态位 |
72 |
|
CG2 |
环境状态位 |
66 |
|
Cet |
环境状态位 |
46 |
|
Fhs |
时间戳 |
1783120000000 |
|
FhR |
时间戳 |
1783120000000 |
|
Cec |
环境状态位 |
63 |
|
CeU |
页面状态 |
visible |
|
CeW |
窗口/布局桶 |
74-75 |
|
bfso_screenOrientation |
屏幕/窗口 |
landscape-primary;0 |
|
bfso_visualViewport |
屏幕/窗口 |
w:1440;h:900;s:1.00;ol:0;ot:0;pl:0;pt:0 |
|
bfso_mediaFeaturePrefs |
CSS/媒体查询 |
dark:0;rm:0;fc:0;ic:0;pc:0;pf:1;hc:1;srgb:1;p3:0 |
|
CGT |
UA-CH |
{"mobile":false,"model":"","platform":"macOS","platformVersion":"14.4.1","uaFullVersion":"149.0.0.0"} |
|
CGZ |
环境状态位 |
123 |
|
Cew |
环境状态位 |
109 |
|
Fi3 |
预留/空值 |
"" |
|
FiE |
WebGPU/GPU |
["depth32float-stencil8","texture-compression-bc","shader-f16",...] |
|
Fie |
WebGPU/GPU |
[objectGPUAdapterInfo] |
|
FiO |
WebGPU/GPU |
getAd:35.7,adFt:0 |
|
FiG |
环境状态位 |
7 |
|
FiU |
内存/存储 |
10737418240;4395630592 |
|
bfso_storageEstimate |
存储额度 |
q:33;u:0;p:0 |
|
Cev |
系统平台 |
MacIntel |
|
Ceo |
浏览器能力 |
true |
|
CeF |
Navigator/环境 |
GoogleInc.(Apple) |
|
CG0 |
环境状态位 |
59 |
|
Fhe |
预留/空值 |
"" |
|
FhO |
预留/空值 |
"" |
|
Fhy |
预留/空值 |
"" |
|
FhX |
预留/空值 |
"" |
|
CGQ |
预留/空值 |
"" |
|
Ceg |
行为/性能计数 |
{"2":35,"5":25,"7":329,"8":32,"52":255,...} |
|
Cez |
报告对象 |
{} |
|
Cem |
行为/报告packed |
[1783120000000,8261,1293,8342,1299,...] |
|
2. API / 代码完整性遥测探针
这一表单独列 API 行为、异常文本、权限状态、hook 痕迹、全局变量摘要和函数源码形态,共 15 个。它们同样是遥测数据:客户端上传的不是“要执行的代码”,而是关键函数/API 在本地环境里的表现证据。服务端可以用这些证据判断 fetch、 XMLHttpRequest、 document.cookie、structured clone、Permissions API、Storage Access API、webdriver/global 变量等有没有被改、被 hook、被自动化框架替换。
|
|
|
|
|
|---|---|---|---|
CES |
自动化/Hook |
webdrivernativehook; |
|
CGl |
函数完整性 |
function(){returnnativeCheck(326)} |
|
CGB |
函数完整性 |
FUNCTION_SOURCE_SNAPSHOT_FAKE_BASE64 |
|
FQP |
API异常探针 |
Failedto execute postMessage:MimeTypeArrayobjectcouldnotbe cloned. |
|
FQF |
API异常探针 |
Failedto execute postMessage:MimeTypeobjectcouldnotbe cloned. |
|
Cee |
API异常探针 |
Cyclic__proto__ value |
|
Ceb |
API探针对象 |
{} |
|
CEo |
API探针列表 |
[] |
|
CeD |
编码/API行为 |
{"1":{"cT":"ArrayBuffer","uT":"ArrayBuffer","e":null},"2":{"e":"EncodingError"}} |
|
FhZ |
自动化/全局变量 |
0,__playwright__binding__,BROWSER_YEAR,__APP_ID__,__LOCALE__,... |
|
CeR |
权限/StorageAPI |
{"st":"rejected","e":"fake storage access error"} |
|
bfso_permissionStates |
权限状态 |
geo:p;cam:p;mic:p;noti:p;clipR:p;clipW:g;midi:pdS |
|
Ced |
自动化检测 |
false |
|
Fhw |
Hook/异常列表 |
[] |
|
FhM |
Hook检测 |
["XMLHttpRequest.prototype.send:68","window.fetch:68","document.cookie:hookGetter,hookSetter"] |
|
CGB、 CGl 这类字段尤其要按“代码完整性遥测”理解:字段里出现函数源码片段,不代表 report body 携带一段要执行的业务代码;它表示客户端把函数源码字符串、VMP stub 形态或原型函数快照作为 anti-tamper 证据上传。服务端消费的是“本地关键函数有没有被改过”的证据,而不是执行这段字段文本。
这类字段的“原始值”也不总是浏览器 API 原文。更准确地说, tlv.fields[].value 是进入 TLV 创建函数的值;它可能已经过字段级字符替换、hash、packed numeric 或 VMP 内部编码。
四、 Ce1 不是 JSON,也不是 AES 密文
Ce1 是这份样本里最容易被误读的字段。当前最稳的结论是:
Ce1是 BODY slot129- TLV tag 是
1000 - 类型是
FIELD_TYPE_STR - 长度类型是
Uint16 - 非空样本长度约
916字符 - 上游是 module
8321的 Picasso/canvas 链路
它的当前样本闭环可以概括成:
Picasso R() canvas draw/state-> raw compact data URL->每字符 XOR 0x5a-> base64-> X/s 字符交换->Ce1/tag1000-> TLV->/report body
脱敏演示:
|
|
|
|
|---|---|---|
|
|
data:image/png;base64,iVBOR_FAKE_10x10... |
|
|
|
RkFLRV9iYXNlNjRfYWZ0ZXJfeG9y... |
|
|
|
RkFLRV9iYXNlNjRfYWZ0ZXJfWHNfc3dhcA... |
|
|
|
dota:imoge/png;bose64,...demo |
|
所以, Ce1 不能按“加密 JSON”去找明文。它更像图形指纹里的 Picasso/canvas payload,当前已闭合的是 data-url 到 Ce1 的可逆扰动层;还没有完全复现的是 raw compact data URL 之前的浏览器绘制状态。
五、 Cem 是 packed numeric/report,不是压缩包
Cem 是另一个容易误判的字段。当前确认:
Cem是 BODY slot161- TLV tag 是
5658 - 类型是
FIELD_TYPE_NUM_ARR - 它不是 gzip
- 它不是 deflate
- 它不是 brotli
- 它不是 JSON
- 它也不是普通可打印文本
脱敏演示:
[1783120000000,8261,1293,8342,1299,8338,1299,...FAKE_NUMERIC_PAYLOAD...]
第一项按 varint 读出来是毫秒时间戳形态,后面跟着成组的行为/报告数字。它和 SFU 里的键盘、鼠标、触摸、civ/event 序列化结构方向一致,但 pB[161] 对应的 exact VMP encoder schema 还没有完全 lift 成可读 JS。
所以对 Cem 的更准确说法是:它是 packed numeric/report/behavior payload。现在缺的是 schema 命名,不是“还差一个解密密码”。
六、VMP 里的 TLV / LV 和 /report 加密流程
这里的 TLV/LV 不是泛泛的“打包格式”四个字。它是 DFP VMP 里把字段值变成 report body 的中间协议层,夹在“浏览器字段采集”和“最终 AES/RSA report body”之间。
按当前样本,相关模块可以这样看:
|
|
|
|
|---|---|---|
1711 / vmp_resolveAllFields |
|
|
1711 / vmp_getDfpData |
|
|
1388 / vmp_createTLV |
|
|
6578 / vmp_createLV |
|
|
7511 |
|
|
1200 |
|
|
3405 |
|
|
4786 |
|
|
5661 |
|
|
当前已确认的总路径如下:
浏览器字段采集->module1711: vmp_resolveAllFields / vmp_getDfpData->module1388: vmp_createTLV(tag, lengthType, value, mask, bsi)->module6578: vmp_createLV(...)->module7511: selected fields RC4-like-> report binary payload->module1200: gzip->module3405: gzip bytes -> base64->module4786: AES-CBC/PKCS7 encrypt(gzipBase64, randomAesKey)->module5661: RSA encrypt(randomAesKey)-> prefix23 || rsaKeyCipher128 || separator14 || aesCipher->final base64-> POST /v2/shpsec/web/report
1. TLV 是字段级协议,不是最终 body 加密
TLV 这一层处理的是“一个字段怎样落成二进制片段”。例如 Ce1、 Cem、 CGj、 CGB 这些字段,都会根据字段表带上 tag、lengthType、type/bsi。
|
|
|
|
|---|---|---|
|
|
1000 |
Ce1。 |
|
|
2 |
|
|
|
Ce1_FAKE_PAYLOAD |
|
|
|
0xFAKE_RUN_MASK |
|
|
|
BSI2_ID |
|
TLV 的要点是:tag、length、value 都不是裸写。当前逆出来的规则可以概括成:
value 规范化->undefined/null变空 bytes-> bigint 变string->object/array 走 JSON.stringify->string走 UTF-8 bytestag-> uint16be(tag)->每字节 XOR masklength->按 lengthType 写 uint8/uint16be/uint32be->每字节 XOR maskvalue-> valueXorByte = mask[0]^ tagLow ^ tagHigh->每个 value byte XOR valueXorByteTLV output-> tagBytes || lengthBytes || encodedValue
所以 TLV 不是最终的 AES/RSA 加密层,它更像 VMP 字段协议里的第一层“字段编号 + 长度 + 值 + 字段级 XOR 混淆”。要解 TLV,必须知道字段表、tag、lengthType、mask 和字段类型。
2. LV 是部分字段的 length-value / report 拼装层
LV 这一层不是所有字段都走。它更像 report payload 里的另一种 length-value 拼装方式,负责把部分字段组织成 report 二进制 payload,并在部分路径上套 RC4-like 处理。
|
|
|
|
|---|---|---|
|
|
vmp_createLV(...) |
6578 暴露的 report/LV 组包函数。 |
|
|
sfu.rc4.call->sfu.rc4.ret |
|
|
|
RC4_KEY_FAKE_PER_RUN |
|
|
|
selected LV/report fields |
/report body 的唯一加密层。 |
|
|
report binary payload |
|
这里容易误判:看到 RC4-like 不等于“整个 report body 是 RC4”。更准确的说法是:VMP 内部对部分字段做 LV 组包和字段级流加密/扰动,随后这些片段才会和其他 TLV 片段一起进入 gzip。
3. gzip 是 report payload 压缩层
TLV/LV 之后得到的是 gzip 前二进制 payload,不是最终请求 body。当前样本里能看到:
|
|
|
|
|
|---|---|---|---|
|
|
10736
|
TLV_LV_BINARY_PAYLOAD_FAKE |
|
|
|
8450
|
GZIP_BYTES_FAKE |
|
|
|
11268
|
GZIP_BASE64_FAKE... |
|
也就是说,gzip 压缩的是已经被 VMP 字段协议处理过的 report payload,不是原始浏览器 JSON。
4. AES / RSA 是外层 report body 加密信封
|
|
|
|
|
|---|---|---|---|
|
|
16
|
k9FAKEbase36z1q |
|
|
|
11268
|
GZIP_BASE64_FAKE... |
|
|
|
11280
|
AES_CIPHER_FAKE_BYTES |
|
|
|
128
|
RSA_FAKE_128_BYTES |
|
|
|
prefix23+128+separator14+aesCipher |
REPORT_FRAME_B64_FAKE_PREFIX... |
/report body。 |
这层才是外层 report body 的主加密信封:
gzipBase64-> AES-CBC/PKCS7(key=random16, iv=random16)-> aesCipherrandom16 AES key-> RSA-1024/PKCS#1 v1.5 public encrypt-> rsaKeyCipherprefix23 || rsaKeyCipher128 || separator14 || aesCipher-> base64->/report body
所以 Shopee DFP report 不是一层算法,而是至少有这些层:
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
因此, /report body 呈现为一段较长的 base64-like ASCII 字符串时,它不是“被截断的 JSON”,而是外层 base64 over packed binary frame。
七、 /report 返回 token 后怎样汇合到搜索请求
当前 pc6 链路修正了一个重要点:后续搜索请求上的两个 token 不是浏览器本地刚重新算出来的两个不同值,而是 /report 返回 token 的复用。
POST /v2/shpsec/web/report-> response JSON:{ code:0, data:{ riskToken:"..."}}-> setResponseText(riskToken)->写入in-memory token slot->持久化 shopee_webUnique_ccd-> getShortToken()/getCacheDfp()-> getClientCheckData()/getDegradedData()->搜索请求头 sz-token / af-ac-enc-sz-token
1. 缓存与 header 映射表
|
|
|
|
|
|---|---|---|---|
|
|
/report
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DAT_FAKE_16HEX |
|
|
|
|
1.12.40 |
|
|
|
|
|
|
2. pipe token 形态
当前样本里的 token 形态可以抽象成:
<base64_16_bytes>|<base64_64_bytes>|<base64_12_bytes>|08|3
脱敏演示:
PART1_FAKE_16B64==|SECOND_FAKE_64B64_SEGMENT_PLUS_PADDING==|THIRD_FAKE_12B64|08|3
这个 pipe token 是服务端消费 DFP 遥测报告后返回的风险结果,不是原始指纹 JSON。当前样本里它从 /report 响应进入本地缓存,再被 SFU wrapper 读出并放进后续业务请求头。
八、SAP 字段要单独看
Shopee 请求上还会出现 SAP 侧字段,它们和 DFP/SFU 会共现在业务请求里,但不是同一条 VMP 链路。
|
|
|
|
|
|---|---|---|---|
|
|
|
SAPID_FAKE_56HEX |
|
|
|
|
SAP_RI_FAKE_HEX |
|
|
|
|
BASE64_FAKE_SEC_BLOB_LEN_2164... |
|
当前文章只把 SAP 放在“业务请求汇合面”里说明,不把它和 DFP 的 /report body、 Ce1、 Cem 混成同一件事。
九、遥测数据怎么上报和被消费
从当前样本看,Shopee DFP report 的服务端消费对象不是一个明文 JSON,而是一份已经封装好的遥测剖面。它把“环境像不像真实浏览器”“API 行为是否正常”“函数有没有被改”“行为队列是否自然”这些信号打包到同一份 report 里。
|
|
|
|
|---|---|---|
|
|
|
CGj
CGT、 Cev、 CGf、 CGw、 CEb、 CGF、 bfso_visualViewport、 bfso_mediaFeaturePrefs、 CEr、 bfso_storageEstimate 等。 |
|
|
|
CEw
CGm、 Ce1、 FQ8、 CEX、 CEt、 CGL、 Ce6、 Ce7、 FiE、 Fie、 FiO 等。 |
|
|
|
bfso_permissionStates
CeR、 FQP、 FQF、 Cee、 CeD、 Ceb、 CEo 等。 |
|
|
|
CGB
CGl、 FhM、 FhZ、 CES、 Ced、 Fhw 等。 |
|
|
|
Fhs
FhR、 Cej、 Ceq、 CeX、 Ceg、 Cem 等。 |
|
|
|
tlv.fields[].value
|
|
|
|
/v2/shpsec/web/report。 |
|
|
data.riskToken |
shopee_webUnique_ccd,后续进入 sz-token / af-ac-enc-sz-token。 |
所以这套东西的核心是“遥测剖面”而不是“token 字符串”。 riskToken 是服务端对这份 DFP 遥测报告的响应结果;业务请求上看到的 sz-token、 af-ac-enc-sz-token 只是这个结果在本地缓存和请求头上的汇合形态。
十、最后怎么理解这条链路
如果只看最终搜索请求,很容易把它简化成:
带上 sz-token 就能请求商品接口
但当前样本说明真实结构更接近:
浏览器侧先构造 DFP 报告->报告经过多层二进制封装上传->服务端根据报告返回 riskToken-> SDK 缓存这个 token-> SFU 在业务请求上注入 token、dat、version、sync-> SAP 另行注入 x-sap-ri / x-sap-sec->最终才到/api/v4/search/search_items
所以,这里谈的不是“单个 token 算法”,而是一套浏览器遥测、报告封装、服务端回 token、本地缓存、业务请求汇合的完整状态机。主线是:Shopee 先消费浏览器侧 DFP 遥测报告,再把服务端返回的 riskToken 缓存并汇合进后续业务请求;SAP 的 x-sap-ri / x-sap-sec 是并行的业务请求安全层。
-


-
公众号:安全狗的自我修养
-
vx:2207344074
-
http://gitee.com/haidragon
-
http://github.com/haidragon
-
bilibili:haidragonx
-
夜雨聆风