十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Shopee接口逆向实战:破解x-sap-Ri与x-sap-Sec动态签名参数

Shopee接口逆向实战:破解x-sap-Ri与x-sap-Sec动态签名参数 1. 内容整体设计与思路拆解1.1 为什么一定要啃下这两个动态参数做Shopee数据采集和接口分析的朋友迟早都会撞上这两道坎x-sap-Ri和x-sap-Sec。它们在Shopee的移动端和Web端请求头里高频出现尤其在搜索、商品详情、订单列表这些核心业务接口中几乎是必带的签名参数。第一次接触的人通常会有一个困惑明明用浏览器打开页面能看到数据直接用请求工具去调接口却疯狂报错返回一堆错乱的数据或者直接给验证码。原因就是缺少了这两个动态参数。服务器端通过它俩来校验请求是否来自真实客户端、请求数据是否被篡改、请求是否在有效时间窗口内发起。说白了这是一道动态门禁不掌握生成逻辑接口请求根本进不去。x-sap-Ri和x-sap-Sec的组合逻辑可以理解为“一个请求的身份证防伪标识”。Ri更像是一个上下文标识用于标示请求来源和会话信息Sec则是核心的安全签名用于校验请求内容是否完整、是否经过篡改。两者需要配合使用单独搞定其中一个没有意义必须成对生成、成对携带后端才会放行。1.2 适合谁来读这篇文章如果你正准备入手Shopee接口分析或者已经写了不少采集脚本却总在签名校验上翻车这篇内容会非常有帮助。文章中会涉及从抓包定位、参数定位、逆向思路到代码复现的完整链路既有思路层面的推导也有可以直接落地的代码示例。我建议你至少具备以下基础会使用抓包工具并看得懂请求体和响应体对JavaScript语法不陌生能看懂常见的JS加密逻辑。如果这些还不太熟建议先把基础补一补再来看因为后面的内容默认你不会被“变量作用域”“函数调用栈”这类概念卡住。有一点需要先申明逆向分析他人的加密参数仅应用于合法合规的技术研究范畴包括但不限于接口联调、数据验证、安全分析等场景。请勿将相关技术用于破坏网站正常运行、非法获取他人数据等用途由此产生的一切后果与本文无关。2. 逆向前的准备工作与工具选型2.1 抓包环境的搭建心得工欲善其事必先利其器。逆向的第一步永远是抓包把请求前后端之间的交互看清楚。针对Shopee这类体量较大的电商平台常规抓包工具我推荐两个方向一是移动端用Charles或Fiddler配合代理抓HTTPS明文流量二是Web端直接用浏览器DevTools的Network面板加Hook脚本。很多人会在抓包环境上踩坑最常见的问题就是抓不到包或者全是乱码。这里有一个非常关键的操作细节一定要正确安装并信任抓包工具的CA证书Android 7.0及以上版本默认不信任用户级证书需要把证书安装到系统级证书目录否则HTTPS流量解不开抓到的一律是TLS加密的密文。讲到这里顺便提一下“解析dwg是不是逆向技术”这类话题很多刚入门的同学容易把“解析某种文件格式”和“逆向工程”混为一谈。在逆向这个语境下真正关心的不是文件格式本身而是程序在运行时如何生成这些字段、如何处理这些数据。回到Shopee的这两个参数我们要分析的是客户端代码里生成它们的那段逻辑而不是简单地“解析”一个静态文件。2.2 工具链清单与选型逻辑以下是我实际操作中验证过好用的工具组合按使用环节区分抓包与流量分析Charles移动端、FiddlerWindows生态、DevTools自带Network面板Web端快捷分析JS逆向调试Chrome DevTools的Sources面板、Selenium或Playwright辅助定位触发时机代码定位辅助Frida动态插桩适合Android端、Object.defineProperty Hook法针对JS层参数追踪数据格式化与验证Postman或Apifox用于接口请求复现Python的requests库用于脚本化调用选型逻辑很简单先用抓包工具确定参数出现的接口范围再用DevTools或者Frida去定位参数生成的代码位置最后通过代码阅读和动态调试还原加密流程。工具不要贪多能解决当前环节的问题就够用。3. 核心细节解析与实操要点3.1 静态定位x-sap-Ri与x-sap-Sec的生成入口拿到一个接口请求后第一步是在DevTools里搜索参数名这是最基础的定位方式。打开Sources面板按CtrlShiftF全局搜索x-sap-Ri或者x-sap-Sec大概率能直接搜到赋值语句所在的位置。但这里有个常见的坑很多站点会把参数名做拼接处理直接搜索完整字段名是搜不到的需要搜索片段比如只搜x-sap或者sap-Ri。如果搜索不到就需要走Hook路线。在页面加载前注入一段JS通过重新定义某个Object属性或者拦截特定函数调用来定位参数的生成位置。这一招非常管用特别是遇到参数通过Object.assign或setHeader方法批量写入请求头的情况。Hook的思路是既然参数最终一定会被设置到请求头上那就拦截所有写入请求头的入口。用Object.defineProperty去Hook XMLHttpRequest对象的setRequestHeader方法或者Hook fetch方法的第二个参数把每次调用的参数名和值全部打出来这样就能看到x-sap-Ri和x-sap-Sec到底是哪一步、哪个函数写入的。3.2 参数生成机制的脱壳与还原思路把生成入口定位到具体函数之后接下来就是读懂它的逻辑。这里我强烈建议把相关代码复制出来格式化后逐行分析不要直接在压缩代码里硬看。Shopee的压缩代码通常经过混淆变量名是a、b、c这类短名函数调用层级也比较深直接看不现实。x-sap-Ri的生成我实际分析下来通常和请求的URL路径、时间戳、会话标识有一定关联类似于一个上下文ID。x-sap-Sec则一般是基于请求体、时间戳、密钥等材料通过特定的哈希或HMAC算法生成。关键点在于找到参与计算的材料有哪些以及算法是MD5、SHA-256还是HMAC-SHA256顺序又如何。这里有个值得关注的细节有些签名算法在运行时还会依赖一个固定的盐值或动态下发的token这个值可能来自某个前置接口的响应。如果你只盯着签名函数本身会发现无论如何都推不出相同结果因为缺少了这个动态因子。所以要养成一个习惯——把生成参数那一刻的全局状态全部记录下来包括全局变量、Cookie、Storage里的内容再去做签名复现。3.3 动态调试中的关键操作手法动态调试是绕不开的一环。拿到JS代码后不要急着看完整逻辑先在可疑的加密函数调用处打上断点然后触发一次请求观察调用栈和局部变量。调用栈能告诉我们函数是哪里调进来的局部变量能告诉我们参与计算的具体数据。对于初学者我给一个最笨但最有效的方法把所有参与计算的变量逐一在Console里打印出来然后把计算结果也打印出来和抓包里看到的目标值做对比。如果对不上就说明漏掉了某个参与项或者算法理解有误这时候要回到代码里重新梳理。反复几次参数生成逻辑就会非常清晰。还有一种情况算法本身不难但嵌入在大型的Webpack模块中牵一发而动全身。这时候最简单的方案就是直接把生成参数的那段函数整体提出来用Node.js在本地跑一遍。只要把所有依赖的函数一并拷贝过来通常就能跑通。4. 实操过程与核心环节实现4.1 从一个真实请求开始逐步推导我拿一个模拟场景来走一遍完整流程。假设我们要分析的接口是搜索接口在浏览器里发起一次搜索后抓到的请求头里出现了这两个参数。第一步先在Sources面板搜索x-sap能定位到一个形如 t.setRequestHeader(x-sap-Ri, T) 的赋值点。接着在赋值点打上断点点击搜索按钮触发请求程序会在赋值那一行暂停。此时观察右侧Scope面板里的T变量它的值是一串长字符串。继续往上翻调用栈找到T是怎么算出来的很快能定位到一个函数大概长这样function generateRi(e) { var t (Date.now() / 1e3).toString(); var n e.join(,); return someEncode(t | n); }这只是一个示意真实代码会复杂很多但思路是一致的拆分参数、确认数据来源、找出拼接规则、定位算法。x-sap-Sec的推导过程也类似差别在于它会引入更大的参与集比如请求体内容、请求头列表、时间戳、随机数等。4.2 利用Frida对Android端进行动态插桩除了Web端Shopee的移动端接口也可以分析而且移动端往往是核心数据的源头。Android端逆向我推荐用Frida进行动态插桩它能在App运行时注入JS代码Hook指定函数并读取参数和返回值。思路是这样的先通过抓包确认App请求中的x-sap-Ri和x-sap-Sec然后用jadx打开App的DEX文件搜索这两个参数名定位到生成函数的Java或Kotlin代码。找到关键函数后用Frida附加上进程对这个函数进行Hook运行时打印入参和返回值逐层还原生成逻辑。Java.perform(function() { var targetClass Java.use(com.shopee.somepackage.SignHelper); targetClass.generateSec.implementation function(data) { var result this.generateSec(data); console.log(generateSec called, data data , result result); return result; }; });这种做法的优势是不需要把整个App反编译成可读的工程只需要定位目标函数打点观察即可。缺点是需要处理反调试、加固、混淆等问题如果App做了加固还得先脱壳才能看到真实逻辑——这个话题展开又是一大篇这里先提一句后续有机会再专门写。4.3 用Python还原完整的签名生成过程无论是Web端还是移动端逆向的最终目的通常是脱离客户端环境复现签名这样才能用脚本批量调接口。我在定位完算法之后习惯用Python把整个流程写出来和抓包的请求逐一对照。下面是一个结构化的示例代表常见的生成套路import hashlib import time def generate_x_sap_ri(path, timestamp): raw_string f{path}|{timestamp} return hashlib.md5(raw_string.encode()).hexdigest() def generate_x_sap_sec(body, timestamp, salt): raw_body body if body else raw_string f{timestamp}|{raw_body}|{salt} return hashlib.sha256(raw_string.encode()).hexdigest() def build_headers(path, body, salt): ts str(int(time.time())) return { x-sap-Ri: generate_x_sap_ri(path, ts), x-sap-Sec: generate_x_sap_sec(body, ts, salt), x-sap-Ts: ts }上面只是示意代码真正的算法和参与序列必须以实际定位到的逻辑为准。但整套思路是一致的先通过抓包拿到一个采样请求再通过在客户端的动态调试确认每一个参与计算的字段最后在本地脚本中还原同样的计算过程。只要采样请求能通过、计算结果和抓包值一致就说明还原成功了。4.4 参数有效性验证与时间窗口控制签名参数之所以叫“动态参数”另一个重要原因是有时效性。同一组签名拿到几分钟后可能就失效了有的甚至只允许在生成后的几秒内使用。这意味着脚本里不能写死签名值每次请求前必须重新计算。我在验证时有一套固定流程先用抓包工具捕获一次真实请求把x-sap-Ri和x-sap-Sec记录下来然后用自己实现的Python脚本重新生成签名发到同一个接口对比返回结果如果两者都成功且数据一致说明签名逻辑正确。接下来测试时间窗口等5分钟再发一次同样的请求如果被拒绝就确认了时效性限制。针对这种场景实现时要注意时钟同步。签名算法依赖时间戳时服务器判定的是“请求到达服务器的时间”与“签名中的时间戳”之差如果本机时间不准很容易被判为请求过期。我在脚本里都会加一个时间校准逻辑def get_time_offset(server_time_url): local_time int(time.time()) server_time request_server_time(server_time_url) return server_time - local_time先把本机与目标服务器的时间差算出来再在生成签名时用校正后的时间去计算能在很大程度上避免时间窗口过期问题。5. 常见问题与排查技巧实录5.1 参数搜索不到怎么办前面提过一种情况就是全局搜不到参数名。这个问题的根源通常是参数名被拼接或者编码过。比如代码里写的是x-sap- Ri这样的形式你直接搜x-sap-Ri当然搜不到。解决办法是搜索前缀或片段比如x-sap、-Ri、-Sec。如果片段也搜不到说明参数名被进一步编码了比如Unicode转义、Base64编码等。这时候建议不要在静态代码里死磕改用动态Hook的方式直接监听请求发出前的header设置通过运行时输出来定位。还有一类情况是请求本身不走XHR而是通过WebSocket或者其他通道发出去的参数生成逻辑可能在WebSocket建立连接时就完成了。排查时可以先确认请求类型再决定Hook的入口。5.2 签名生成了但接口仍然拒绝签名算法看起来没问题生成的值也和抓包一致但请求发出去还是被拒。遇到这种问题第一反应应该是比较请求头和抓包请求的差异一个字符都不能差。我踩过的一个坑是请求头里多个空格或者换行都会导致签名校验失败。其次要关注的是参与签名的材料是否完整。有些签名算法会把所有的header名称按字母序一起计算进去或者会把Cookie中的部分字段作为输入。只实现了显式逻辑、漏掉了隐式输入签名自然不对。排查这个问题的比较高效的方式是用浏览器原生环境做一次基准请求把请求头完整复制下来和自己的脚本请求做逐字段对比。差异点往往就是问题所在。5.3 常见问题速查表问题现象可能原因排查方向全局搜不到参数名参数名被拼接/混淆/编码搜前缀、动态Hook定位签名值不一致参与计算的字段缺失或顺序不对断点观察局部变量逐项比对请求立刻被拒时间戳过期或本地时钟偏差校准时间偏移缩短生成与发送间隔接口返回验证码请求特征被识别为脚本检查User-Agent、请求频率、行为轨迹同一签名时好时坏动态盐值/Token参与签名找前置接口获取动态因子加密函数加载不出来Webpack模块依赖缺失补齐自执行函数与依赖模块5.4 提升逆向效率的几点建议最后分享几个提升效率的小习惯。第一个是建立参数追踪表把每次分析到的参数名、来源、生成算法、参与字段、有效期全部记录下来。遇到平台升级时这个表可以帮助快速定位变化点而不是从头再来一遍。第二个习惯是善用本地复现。一旦把参数生成逻辑理清楚就尽快用脚本在本地复现用本地脚本和抓包结果做比对。本地复现成功之后整个链路的可控性会大幅提升后面调试和扩展都会轻松很多。第三个习惯是对请求频率保持克制。不管做接口分析还是数据采集都尽量模拟真实用户的行为频率避免高频请求触发风控。风控是动态对抗触发之后会显著增加后续分析的成本得不偿失。我个人在实际操作中最深的体会是这类动态参数逆向并不存在“一次搞定、一劳永逸”的方案平台随时可能调整参数的生成逻辑、参与字段或者算法本身。关键是掌握整体思路和调试技巧这样即使参数变了也能相对快速地重新定位和分析。希望这篇内容能帮你少踩一些我踩过的坑把精力集中在真正值得研究的技术细节上。
返回列表