ARTICLE DETAIL

资讯详情

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

小程序接口签名机制逆向分析:从抓包到算法还原

小程序接口签名机制逆向分析:从抓包到算法还原 1. 项目背景为什么航班查询接口要上签名机制1.1 从飞常准的接口设计说起飞常准是国内比较知名的航班动态查询工具它的微信小程序提供了实时航班状态、起降时间、延误信息、航站楼登机口等查询能力。这些数据背后是一整套接口体系而其中最关键的防护层就是签名机制。我最初接触这个课题是在做小程序安全评估的时候。要判断一个接口是否存在越权、重放、批量爬取等风险绕不开签名机制——它是接口安全的第一道闸门。不夸张地说签名这关过了后面几乎畅通无阻反过来签名校验做得够扎实外面的人想碰这些接口难度会直线上升。这篇内容不是教你怎么去薅某个平台的航班数据而是从技术研究的角度把小程序接口逆向分析的方法论完整梳理一遍。无论你是做小程序开发的工程师、刚入门安全方向的学生还是单纯对接口防护机制感兴趣的技术爱好者这套思路和流程都可以直接迁移到你自己的项目、自己的接口测试里。我会把每一步为什么这么做、有哪些坑、怎么验证结果全部摊开来讲。1.2 签名机制到底在防什么要理解签名机制先得搞清楚它防的是谁。小程序这种场景有个天然特点客户端代码打包下发到用户手里是完全可以被反编译的。攻击者根本不需要操作界面直接照着抓到的请求格式模拟发送HTTP请求就能调你的接口。如果没有签名校验会出什么问题最常见的是三类数据被批量爬取——写个循环脚本几分钟就能把大量航班动态数据搬走请求被恶意重放——抓到一条合法请求反复提交刷接口、刷业务逻辑参数被篡改——改掉查询参数尝试越权比如把航班号改掉看别人的数据或者把日期改掉绕过某些限制签名机制的核心目标就是让每个请求都携带一个只有合法客户端才能算得出来的值。服务端收到请求后用同样的算法重新计算签名能对得上就放行对不上就拒绝。这样一来盲目构造的请求包就失去了意义。理解了这层逻辑你就知道后面所有逆向分析的着力点在哪里了。2. 小程序接口分析的完整技术路线2.1 抓包工具选型与基础配置要分析接口第一步永远是抓包。微信小程序走的是HTTPS协议常规抓包工具就能处理前提是证书装到位。我常用的组合是Charles加手机代理Fiddler也可以看个人顺手程度。这里有个前提要说明有的小程序做了SSL Pinning证书绑定常规代理方案抓不到明文。实测飞常准小程序没有做这层防护普通抓包就能看到请求内容这在商业小程序里算是比较常见的状态。碰到有Pinning的目标得先过掉证书校验才能继续那是另一个话题这次先按下不表。配置抓包环境的基本步骤我整理了一下电脑安装Charles或Fiddler开启SSL Proxying生成并信任CA证书手机和电脑连同一个局域网手机手动设置HTTP代理指向电脑IP加端口手机浏览器访问证书下载地址安装并信任该证书打开微信小程序随便发起一个查询观察抓包工具里的流量有个高概率踩中的坑安卓7.0以上系统默认不信任用户安装的证书如果HTTPS请求怎么也抓不到十有八九就是这个原因。常见解法是把证书装进系统证书目录需要root或者用已经配好证书的模拟器来跑。iOS上相对宽松一点但也要去设置里信任描述文件这点容易漏。提示抓包只是手段真正的难度在于从海量流量里精准定位到目标请求再抽丝剥茧还原出背后的计算逻辑。2.2 流量定位与关键请求识别打开小程序随便点几下抓包工具里会瞬间涌入几十上百条请求静态资源、埋点、广告、业务接口混在一起。我习惯先按类型粗筛一遍静态资源图片、CSS、JS文件——先忽略接口请求返回JSON数据的——重点关注埋点和统计请求——暂时忽略除非后面需要定位航班查询接口有个小技巧看URL关键词。飞常准的接口路径里基本会包含flight、schedule、dynamic这类词一眼就能认出来。找到之后把请求参数和返回数据放在一起对照能很快确认哪个接口对应哪个功能。定位到目标请求后先别急着分析签名。第一件事是把完整的请求信息记录下来URL、请求方法、请求头、全部参数、返回内容。最好直接把curl命令从抓包工具里导出保存这些是后续所有分析工作的事实基础。我之前吃过亏分析到一半发现初始请求数据被工具自动清理了又得重新抓一遍浪费时间。3. 签名机制的核心拆解3.1 签名参数的组成结构大多数小程序的签名请求参数名基本逃不开sign、signature、token、nonce、timestamp这几类。飞常准的请求里有一个明显的签名字段同时配套一个时间戳字段这是非常经典的签名设计模式。从抓包里看到的一个典型签名请求长这样参数名与值已做脱敏处理{ flight_no: CA1234, date: 2025-06-15, timestamp: 1749000000, nonce: a1b2c3d4e5f6, sign: 9f8e7d6c5b4a3f2e1d0c }timestamp是请求发起时的Unix时间戳nonce是一次性随机字符串sign就是整个签名机制的产出物。服务端拿到请求后会用自己持有的密钥按相同算法重新计算sign与请求里的值比对同时检查时间戳是否在容忍窗口内。时间戳过期或者签名对不上直接拒绝。那sign到底是怎么算出来的这才是核心问题。虽然各平台的实现细节不同但骨架高度一致参数排序、拼接、加密。搞清楚这套骨架就等于拿到了解开所有类似机制的钥匙。3.2 从请求参数到签名字符串小程序接口的签名算法本质上是一个参数排序加拼接加加密的过程。通用步骤如下收集所有业务参数剔除签名本身和空值参数按参数名的字典序ASCII码顺序排序用keyvalue的形式拼接成字符串在拼接结果的首尾或中间插入AppSecret之类的密钥对拼接结果做MD5、SHA256或HMAC计算结果转成十六进制或Base64字符串得到sign说起来简单实际还原的时候全是细节。比如拼接用的连接符到底是还是空字符参数之间要不要加分隔符密钥加在开头还是结尾还是中间这些都会直接影响最终结果。而且你只有输入参数和最终输出两个端点中间链路全靠推断所以每一步都要小心验证。这类签名机制的薄弱点在于密钥必须存在于客户端代码里。只要客户端能正常算出签名密钥就有办法被提取出来。服务端能做的只是让提取过程变得困难一些。3.3 时间戳与nonce的联动校验逻辑时间戳和nonce这两个字段很多人会忽略其实它们大有讲究。时间戳是为了防重放。服务端拿到timestamp后会判断它和当前时间的差值超出容忍窗口直接拒绝。所以你在还原签名算法的时候时间戳一定要用当前时间不能写死。用旧时间戳发起请求服务端一秒钟都不会犹豫直接返回错误。nonce是为了防时间窗口内的重放。理论上只要时间戳合法同一秒内重放同一个请求是可能被接受的。加上随机nonce之后服务端可以把已用过的nonce缓存起来发现重复就判定异常。设计严谨的系统nonce还会有过期时间避免缓存无限膨胀。在逆向分析时有个值得注意的点服务端到底校验了哪些参数不同平台策略不同有的把所有业务字段全部纳入签名有的只签关键字段。判断方法很简单——改某个参数的值看签名是否还通过。改了之后签名失效说明这个字段参与签名没变说明它不参与。这一步的结论直接决定你后面构造的拼接串对不对。4. 实操环节从拿到请求到还原签名逻辑4.1 定位小程序代码里生成签名的位置光看不行动不行。要真正还原签名算法必须找到小程序代码里生成签名的那段逻辑。微信小程序的代码包是wxapkg格式里面是编译后的JavaScript代码。拿到这个文件有几条常见途径从手机微信缓存目录提取需要root或备份能力、通过脱壳工具从调试环境提取、或者从PC端微信缓存目录提取。拿到之后用解包工具拆开就能看到一堆JS文件。这些JS文件通常经过了压缩和混淆但这不影响搜索。在解包后的JS文件里搜sign、md5、sha256、sort、timestamp这些关键词通常能快速锁定可疑代码段。我自己的经验是先从sign这个词搜起命中率极高——不管混淆得多厉害签名结果最终要赋值给名为sign的参数这个映射关系绕不开。如果代码里把签名参数别名改成了s或sig那就多花点时间去请求构造的地方找线索。4.2 还原加密流程的通用思路找到关键代码后如果代码没有重度混淆一眼就能看出算法结构。但现实往往是代码被压缩成一行变量名全是a、b、c逻辑链直接被拉平。我的处理步骤一般是先用js-beautify或prettier把代码格式化在格式化后的代码里找关键函数插入日志输出中间变量用Node.js搭一个本地环境把可疑函数单独拎出来执行输入抓包拿到的真实参数看输出是否与抓包结果吻合这里强烈建议搭一个本地的Node.js调试环境。很多小程序代码跑在微信特有的运行环境里直接运行会报环境缺失错误。我的做法是把签名相关函数抽取出来后用webpack打包缺失的全局变量用mock补上在Node里跑通。跑通之后做一次交叉验证——输入真实请求的全部参数算出的签名如果和抓包值一致说明还原对了不一致说明还有细节没补全。交叉验证是整个逆向过程中最有成就感的一步也是最关键的一步。它意味着你不只是猜了个大概而是真正理解了算法的完整链路。4.3 用交叉验证确认签名算法假设某个接口的签名逻辑是参数按字典序排序拼接尾部追加密钥再做MD5那么验证脚本的核心逻辑大致是这样的const crypto require(crypto); function generateSign(params, secret) { const keys Object.keys(params).sort(); let base ; keys.forEach(key { // 跳过空值和签名相关字段 if (params[key] ! key ! sign key ! nonce) { base ${key}${params[key]}; } }); base base.slice(0, -1); // 去掉末尾多余的 base secret; // 尾部拼上密钥 return crypto.createHash(md5).update(base).digest(hex); } // 用抓包到的参数做验证 const sampleParams { flight_no: CA1234, date: 2025-06-15, timestamp: 1749000000 }; const sign generateSign(sampleParams, your_secret_placeholder); console.log(sign);如果输出的值跟抓包里的sign一致签名机制的基本逻辑就梳理清楚了。不一致也正常可能是过滤规则不同、排序方式不同、密钥藏在别处或者加密算法不是MD5而是别的。继续用控制变量法逐项排查就好。注意上面这段代码是研究签名算法通用原理的示例不代表任何具体平台的真实实现。涉及真实系统时请务必在你拥有或已获授权的目标上测试。4.4 签名还原之后的完整验证链路签名算法确认之后完整验证链路应该闭环用当前时间戳、随机nonce、真实业务参数生成一个新的sign把完整的请求发送到服务端看是否正常返回业务数据如果返回正常说明整个签名链路的理解是正确的这个闭环实验非常关键。它能证明你不是碰巧撞对了一次而是真正吃透了整个机制。同时它也是检验参数过滤规则理解是否完整的终极标准——某个参数在抓包里看到了、但实际不参与签名你把它加进拼接串签名就会错。反过来某个隐藏参数参与签名但你没发现签名照样对不上。很多人在这个环节翻车最常见的现象是脚本里算出来的签名跟抓包完全一致但一发请求就报签名错误。这种情况通常踩中了编码坑——URL编码、大小写、空值处理任何一个不对都能让服务端算出来的结果跟你不一样。5. 常见问题排查与避坑指南5.1 抓包环境里的高频问题做这类分析大量时间会耗在环境问题上真正分析算法的占比反而不高。我把几个高频问题整理成了表格都是实际踩过的问题现象可能原因解决方案小程序请求抓不到安卓7.0不信任用户证书把证书装进系统目录或使用已配置好的模拟器抓到的全是CONNECT请求SSL代理没有生效检查SSL Proxying是否开启域名是否在代理列表内请求返回400或403签名时间戳过期确认脚本里用的是当前时间不是抓包时的旧时间戳接口返回签名错误拼接字段与校验字段不一致逐字段排查确认哪个参数参与签名、哪个被过滤请求正常但返回加密数据业务层有二次加密定位解密函数一般在响应拦截器里还有一个容易忽略的点小程序的部分请求走的可能是WebSocket或其他通道跟普通HTTPS混在一起。定位时要留意Content-Type和请求方法GET、POST、PUT都可能存在别只盯着POST看。5.2 混淆代码的应对思路现在不少小程序都会做一定程度的混淆。碰到重度混淆时硬啃代码效率极低我一般换几条思路动态调试在小程序运行环境里hook关键函数观察运行时行为行为分析不分析代码直接分析网络层的输入输出关系控制变量法每次只改一个参数观察签名结果的变化规律控制变量法在应对复杂算法时特别好用。怀疑某个参数参与拼接就单独改这个参数的值看签名变化还是不变。变了确认在签名范围内没变说明被过滤或压根没参与。用这种方式即使完全不看代码也能把签名规则摸个大概。另外搜索关键词别只盯sign。secret、key、token、appKey、publicKey这些词都值得一试。密钥往往以字符串常量的形式出现在代码某个角落找到它往往只是时间问题。代码里搜不到就去内存里找去请求头里找密钥出现的形态是多种多样的。5.3 签名还原中最容易忽略的细节几个容易被忽略的细节我单独拿出来说都是踩过坑的第一参数拼接时的空值处理。不少算法会先过滤空字符串、null、undefined或者剔除某些特殊字段。过滤规则和你在代码里看到的不一定完全一致需要逐项验证。第二数组和对象参数的序列化方式。参数值是数组或对象时拼接形式可能是a1,2,3也可能是a[1,2,3]。序列化方式不同拼出来的字符串完全不一样签名结果自然对不上。第三编码与大小写。中文参数需要URL编码编码前后的字符串完全不同。签名的输出也有大小写之分有的算法输出大写十六进制有的是小写。这个细节不仔细看往往要排查半天。第四请求头里的参与字段。有时候签名范围不限于请求体User-Agent的某一段、X-Wx-Token之类的自定义请求头也可能参与签名计算。所以分析的时候别只盯着请求体请求头同样要过一遍。6. 安全视角的思考与合规边界6.1 从逆向到防护的思维转换做逆向本身目的不该只是拿数据。我花时间梳理这类课题更多是为了站在攻击者视角理解防护方案的薄弱环节再反过来强化自己的系统设计。说白了不懂攻击的防守多半是纸糊的。如果你正在设计小程序接口下面几条建议可以直接落地签名密钥不要写死在客户端代码里——只要客户端能拿到就一定能被分析出来只是时间成本的问题尽量用HMAC而不是简单MD5拼接——HMAC的抗碰撞能力更强密钥管理也更规范时间戳容忍窗口建议控制在5分钟以内并且配合nonce去重核心业务接口考虑加SSL Pinning作为第二道防线服务端做好限流和风控同一账号高频请求要能触发告警没有绝对安全的客户端只有层层叠加、让攻击成本高过收益的防护体系。6.2 逆向分析的合法边界这个部分必须说清楚。逆向分析本身不是违法行为但使用方式直接决定合规性。安全领域有个基本共识对你自己拥有或已获书面授权的系统做研究是合规的未经授权对他人商业系统做攻击性测试、绕过认证、批量爬取数据则可能触碰法律红线。尤其是涉及个人隐私、商业机密的场景风险远大于收益。所以如果你是在做自己的小程序安全测试、学习接口安全原理、或者参与合规的众测项目这套思路完全适用。但如果你只是想拿别人的数据搞灰色业务那这条路走不通也不应该走。6.3 这套方法论还能迁移到哪些方向签名机制的分析方法不只适用于航班查询类小程序。电商小程序、社交应用、企业办公系统底层架构思路大同小异。掌握了这套方法论换个目标只是按同样的流程重新走一遍而已。再往外延伸还能接触到更有意思的课题代码混淆对抗、反调试机制、风控系统设计、服务端签名校验的高性能实现。每个方向深入下去都足够独立成文。安全这行的特点就是这样一个点扎下去能带出一整片技术面。我在实际研究这类问题时最大的体会是破解签名机制这件事真正的难点从来不在加密算法本身——MD5、SHA256、HMAC都是公开算法文档一抓一大把。难点在于你要极有耐心地处理参数顺序、过滤规则、编码方式这些看似不起眼的细节。差一个字段对不上整个签名就是错的没有任何中间状态。这种全对或全错的特性逼着人变得严谨也算是一种额外的收获。如果你此刻正卡在某个签名验证的超时或错误上我的建议是先把心静下来把抓包数据一格一格对照从排序、过滤、拼接、加密四个环节逐个排查。方法永远是那套方法剩下的是细心问题。希望这篇内容能帮你少走一些弯路也欢迎你在实操中遇到有意思的细节时回来一起交流。
返回列表