逆向电商平台加密签名参数:以FastMoss的fm-sign为例

发布时间:2026/7/30 16:37:34
逆向电商平台加密签名参数:以FastMoss的fm-sign为例 1. 项目概述当电商数据爬取遇上加密参数最近在做一个电商数据分析的项目目标平台是FastMoss。这个平台上有大量店铺和商品数据对于市场研究来说是个宝库。但和很多现代电商网站一样FastMoss在保护其核心数据接口上下了不少功夫其中最让人头疼的就是那个叫fm-sign的加密参数。无论是查询店铺列表、搜索商品还是获取店铺详情几乎所有重要的API请求里都带着这个参数。没有它服务器直接返回403或者一个空数据包爬虫工作瞬间陷入僵局。这个fm-sign参数本质上是一个签名Signature。它的作用是验证请求的合法性确保请求来自其官方前端而非我们这样的“外来者”。它通常由请求的URL、时间戳、请求参数以及一个只有服务器才知道的密钥Secret Key通过特定算法如HMAC-SHA256计算得出。前端在发起请求前会实时计算这个签名并附在请求头或参数里后端收到请求后会用同样的逻辑再算一遍进行比对。如果对不上请求就被视为无效或恶意请求。这种机制在如今的Web开发中非常普遍尤其是在涉及敏感数据或需要防刷的接口上比如电商、社交、金融等平台。逆向破解这个参数目的不是为了“攻击”网站而是为了在合规的数据采集框架下例如遵守Robots协议控制请求频率不进行恶意刷取能够自动化地获取公开数据进行分析。这对于竞品分析、价格监控、市场趋势洞察等商业智能应用至关重要。如果你也遇到过类似的加密墙或者对Web逆向、JavaScript调试感兴趣那么接下来的内容应该能给你提供一套清晰的实战思路。整个过程就像一场数字侦探游戏我们需要在前端浩如烟海的JavaScript代码中找到生成那个关键“密码”的“钥匙”和“算法”。2. 逆向工程的核心思路与准备工作逆向一个前端加密参数核心目标就是找到生成这个参数的JavaScript代码逻辑并能在我们自己的环境如Node.js、Python中复现它。这听起来像大海捞针但只要有正确的工具和方法路径是清晰的。我们的思路可以概括为“定位、分析、提取、复现”四个步骤。首先我们需要强大的工具。浏览器开发者工具Chrome DevTools是我们的主战场。其中“网络”Network面板用于捕获和分析HTTP请求特别是观察fm-sign出现在哪个请求的哪个位置是Headers里的X-Sign还是Query Parameters里的sign。而“源代码”Sources面板和“控制台”Console面板则是我们深入JavaScript腹地的关键。此外我会强烈推荐使用一些专门用于反混淆和调试的浏览器扩展比如“ReRes”可以本地替换线上JS文件方便我们修改和调试、“EditThisCookie”管理Cookie有时密钥或种子藏在Cookie里。对于复杂的、经过混淆和压缩的代码一个能格式化Pretty PrintJS代码的IDE或在线工具也必不可少。在开始逆向之前我们必须先理解常见的加密签名生成模式。这能帮助我们在看到一堆乱码般的代码时快速识别出关键部分。最常见的模式包括基于时间戳的哈希sign md5(timestamp secret_key)或sign hmac_sha256(path ? sorted_params, secret_key)。这里的secret_key可能是一个固定字符串也可能是从某个接口或Cookie动态获取的。包含请求指纹签名可能不仅包含参数还会混入一些浏览器指纹如User-Agent的某部分、屏幕分辨率等增加逆向难度。多层加密与混淆先对参数进行某种编码如Base64再进行哈希或者使用非标准的自定义哈希算法。WebAssemblyWasm最高级别的防御将核心算法编译成Wasm模块JavaScript只负责调用。这大大增加了逆向难度需要分析Wasm二进制文件。对于FastMoss的fm-sign我们首先假设它属于第一种或第二种常见模式。我们的突破口就是找到那个执行签名计算的JavaScript函数。注意所有逆向分析工作都应仅针对公开可访问的前端JavaScript代码进行。切勿尝试破解服务器端私有密钥、入侵服务器或进行任何违反法律法规和网站服务条款的操作。本文讨论的技术仅用于安全研究和学习目的。3. 定位加密函数的关键技巧打开FastMoss网站并进入开发者工具的“网络”面板。清空记录然后进行一次会触发携带fm-sign参数的请求操作比如点击“店铺排行”或进行搜索。在请求列表中找到目标请求通常是XHR/Fetch类型点击查看其“标头”Headers和“负载”Payload/Payload。确认fm-sign的位置和值。接下来是最关键的一步搜索。由于代码被压缩函数名和变量名都是单字母直接搜索“fm-sign”或“sign”可能无果。我们有几种搜索策略搜索参数名在“源代码”面板中使用全局搜索CtrlShiftF搜索fm-sign这个字符串。如果它作为键key出现在参数对象里比如{‘fm-sign’: ‘xxx’}我们就能找到构建请求参数的地方。搜索加密算法特征搜索MD5、SHA、HMAC、encrypt、sign、CryptoJS一个常用的前端加密库等关键词。即使变量名被混淆这些算法库的函数名或字符串常量可能保留。搜索固定字符串或数字观察fm-sign的值如果它总是以固定字符开头或结尾或者长度固定可以尝试搜索这些特征值的一部分需转义。但通常签名值每次请求都变此法不常用。XHR/Fetch断点在“源代码”面板找到“事件监听器断点”Event Listener Breakpoints展开“XHR”或“Fetch”勾选“readystatechange”或“fetch”。然后再次触发请求代码会在发起网络请求前暂停。此时调用栈Call Stack会显示是哪个函数发起的请求我们可以一步步往回追溯找到生成签名的代码。Hook技术这是一种更高级且高效的方法。在控制台Console中我们可以注入代码来“钩住”Hook关键函数。例如我们可以重写XMLHttpRequest.prototype.send或window.fetch方法在请求发出前打印出它的参数从而定位到添加fm-sign的代码位置。// 示例Hook fetch 方法 (function() { var originalFetch window.fetch; window.fetch function() { console.log(‘Fetch called with arguments:‘, arguments); // 可以在这里下断点 debugger; return originalFetch.apply(this, arguments); }; })();执行上述代码后再触发请求控制台会打印出详细的调用信息。查看arguments如果请求的配置对象如Request或init参数中包含fm-sign我们就能看到它被添加的时刻和上下文。在我的实战中通过Hookfetch并配合“XHR断点”我成功将范围缩小到了一个名为main.xxxxxx.js的压缩文件中的某几行代码。格式化代码后发现了一个关键函数调用o n(‘a1b2‘).default.sign(t, e)。这里的n看起来像是Webpack的模块加载器‘a1b2‘是模块ID.sign是导出的签名方法t和e是参数。4. 深入分析加密算法与参数构成找到疑似签名函数后下一步就是深入这个模块理解它的输入和输出。我们需要找到模块‘a1b2‘的定义。在格式化后的JS文件中搜索‘a1b2‘:或“a1b2“:通常能在Webpack的模块定义数组或对象中找到它。果然我找到了类似这样的代码块/* 模块ID a1b2 */ (function(module, exports, __webpack_require__) { “use strict“; Object.defineProperty(exports, “__esModule“, { value: true }); exports.default void 0; var _cryptoJs __webpack_require__(“c3d4“); var _default { sign: function(params, timestamp) { // 1. 对参数对象进行按键名排序 var sortedKeys Object.keys(params).sort(); var strToSign ““; for (var i 0; i sortedKeys.length; i) { var key sortedKeys[i]; if (params[key] ! null params[key] ! undefined) { strToSign key ‘‘ params[key] ‘‘; } } // 去除末尾的‘‘ if (strToSign.endsWith(‘‘)) { strToSign strToSign.slice(0, -1); } // 2. 拼接上时间戳和路径假设从上下文中获取了apiPath var apiPath window.__API_PATH__ || “/api/v1/search“; // 示例 var rawSignStr apiPath ‘?‘ strToSign ‘t‘ timestamp; // 3. 使用HMAC-SHA256进行加密密钥似乎是一个固定值 var secret “fastmoss_secure_key_2023“; // **注意这是示例真实密钥不同** var hash _cryptoJs.HmacSHA256(rawSignStr, secret); // 4. 将哈希结果转换为十六进制字符串 var sign hash.toString(_cryptoJs.enc.Hex); return sign; } }; exports.default _default; })分析这段代码已做简化脱敏我们可以清晰地看到fm-sign的生成逻辑参数排序与拼接将请求的查询参数GET或表单参数POST对象按照键名key的字母顺序进行排序。然后拼接成key1value1key2value2的标准格式。这一步是为了保证无论前端以何种顺序传递参数后端都能以同样的规则生成签名进行比对。构造待签名字符串将API请求的路径Path与排序后的参数字符串、以及一个时间戳t拼接起来。格式类似于/api/v1/shop/list?keywordabcpage1t1646389471123。这个t参数通常就是当前时间戳毫秒级也会作为独立参数发送到服务器。HMAC-SHA256加密使用CryptoJS.HmacSHA256算法用某个密钥secret对上面构造的字符串进行加密生成一个哈希值。这里的secret是关键中的关键。在示例中它被硬编码在代码里但在真实场景中它可能被混淆、拆分成多个部分、或从首次请求的响应中动态获取。输出格式化将二进制哈希结果转换为十六进制Hex字符串这个字符串就是最终的fm-sign值。至此算法逻辑已经明朗。但还有一个重大问题那个secret密钥在混淆的代码里可能不是明文的“fastmoss_secure_key_2023“。它可能被编码如Base64、被拆分成数组然后拼接、或者隐藏在某个全局变量、Cookie甚至第一次请求的响应数据里。我们需要继续追踪这个secret的来源。5. 密钥追踪与算法复现追踪密钥需要耐心和细致的代码阅读。在找到的签名模块附近搜索secret、key、salt等词汇。查看_cryptoJs.HmacSHA256的第二个参数是什么。它可能是一个变量比如var s ‘f‘ ‘a‘ ‘s‘ ‘t‘ ...通过字符串拼接来隐藏也可能是一个函数调用的返回值。在我的案例中通过进一步搜索和调试发现secret是通过一个名为getSecurityKey的函数获取的。这个函数会先检查localStorage中是否有缓存如果没有则发起一个特定的初始化请求例如GET /api/init来获取一个临时的token然后用这个token和客户端的一些固定信息如App版本号通过一个简单的变换得到最终的secret。这个token有过期时间过期后需要重新获取。实操心得遇到这种动态密钥不要慌。通常第一次获取密钥的接口本身是不需要签名的或者使用更简单的签名甚至没有。我们可以先用浏览器正常访问网站在开发者工具的“网络”面板中找到这个初始化请求记录下返回的token。然后在我们的爬虫脚本中第一步就是模拟这个初始化请求拿到token计算出当前的secret。这样后续所有需要签名的请求就都能处理了。搞清楚了算法和密钥来源我们就可以开始用Python以requests和hashlib库为例或Node.js来复现这个签名逻辑了。以下是Python版本的复现代码示例import hashlib import hmac import time import urllib.parse def generate_fm_sign(api_path: str, params: dict, secret_key: str) - (str, int): “““ 生成FastMoss风格的fm-sign签名。 参数: api_path: API路径如 ‘/api/v1/shop/list‘ params: 请求参数字典 secret_key: 从初始化接口获取的密钥 返回: (signature, timestamp) 元组 “““ # 1. 生成当前时间戳毫秒 timestamp int(time.time() * 1000) # 将时间戳加入参数 all_params params.copy() all_params[‘t‘] timestamp # 2. 对参数按键进行排序并拼接 sorted_keys sorted(all_params.keys()) param_list [] for key in sorted_keys: value all_params[key] if value is not None: # 注意值可能需要URL编码取决于原前端如何处理 # 通常拼接时用原始值但发送请求时需要编码。这里按原前端逻辑来。 param_list.append(f“{key}{value}“) query_string ““.join(param_list) # 3. 构造待签名字符串 string_to_sign f“{api_path}?{query_string}“ # 4. 使用HMAC-SHA256计算签名 # 注意secret_key需要转换为bytes string_to_sign也需要转换为bytes secret_bytes secret_key.encode(‘utf-8‘) message_bytes string_to_sign.encode(‘utf-8‘) hmac_obj hmac.new(secret_bytes, message_bytes, hashlib.sha256) signature hmac_obj.hexdigest() # 获取十六进制哈希值 return signature, timestamp # 使用示例 if __name__ “__main__“: # 假设的密钥实际应从初始化接口获取 my_secret “dynamic_secret_from_init_api“ # 请求参数 my_params { “keyword“: “蓝牙耳机“, “page“: 1, “pageSize“: 20 } api_path “/api/v1/search“ sign, ts generate_fm_sign(api_path, my_params, my_secret) print(f“生成的签名: {sign}“) print(f“使用的时间戳: {ts}“) # 最终请求URL应构造为f“{base_url}{api_path}?keyword蓝牙耳机page1pageSize20t{ts}fm-sign{sign}“这段代码完全复现了我们在前端JavaScript中分析的逻辑。关键在于确保每一步的细节都与前端一致参数排序规则通常是字典序、空值处理、字符串拼接格式是否包含?是否在末尾有多余的、以及编码问题。6. 完整爬虫流程集成与注意事项将签名生成函数集成到一个完整的爬虫流程中通常包含以下步骤会话维持使用requests.Session()来保持Cookie模拟浏览器会话。许多网站的初始化token或后续请求会依赖Session Cookie。获取动态密钥首先访问初始化接口如/api/init或/config从响应中提取生成secret所需的信息如token、nonce等并按照前端逻辑计算出secret_key。将这个secret_key和可能的token过期时间缓存起来。构造请求为每个需要签名的API准备参数。生成签名调用generate_fm_sign函数传入API路径、参数和secret_key得到fm-sign和timestamp。发送请求将timestamp作为t参数fm-sign作为fm-sign参数与其他原始参数一并发送请求。注意参数放在查询字符串GET还是请求体POST中需与前端保持一致。处理响应解析返回的JSON数据。错误处理与重试如果返回签名错误如HTTP 403或特定的错误码可能是token过期。此时应清除缓存的secret_key回到步骤2重新获取。请求频率控制严格遵守伦理爬虫规范在请求间添加随机延时如time.sleep(random.uniform(1, 3))避免对目标服务器造成压力。常见问题与排查技巧实录问题1生成的签名总是无效。排查这是最常见的问题。使用对比法。用浏览器正常操作一次在开发者工具中记录下精确的请求URL包括所有参数和值以及请求发出的时间。同时用你的脚本在同一时刻时间戳相同用相同的参数计算签名。然后逐字对比两个待签名字符串string_to_sign是否完全一致。特别注意参数值的类型数字1vs 字符串“1“、空格、编码%20vs、参数的顺序、甚至路径末尾的斜杠/api/pathvs/api/path/。问题2初始化接口也需要签名陷入死循环。排查仔细检查初始化接口的请求。它可能使用一种更简单的签名方式如简单的MD5或者根本不带签名而是依靠其他验证如HTTP Referer头。也可能密钥是固定的硬编码在另一个JS文件里无需初始化请求。问题3算法看起来更复杂似乎不是标准HMAC。排查如果搜索CryptoJS没找到可能网站使用了自定义的加密函数或WebAssembly。此时HookMath.random、Date.now或Array.prototype.join等基础函数可能有助于定位。或者可以考虑使用自动化工具如Puppeteer或Selenium直接运行浏览器环境让JavaScript自然执行并获取结果但这会牺牲一些效率和资源。问题4成功几次后IP被封锁。应对这是反爬策略。需要添加代理IP池并进一步降低请求频率模拟更真实的人类行为如随机滑动鼠标、随机等待时间。检查请求头是否完整模拟了浏览器User-Agent,Accept,Accept-Language,Referer等。逆向fm-sign这样的参数是一个典型的Web逆向工程案例。它考验的是耐心、细心和对HTTP协议及前端JavaScript运行原理的理解。整个过程从模糊的猜测开始通过工具定位、逻辑分析、代码还原最终得到一个清晰、可复现的算法模型。这种能力在需要与复杂前端接口打交道的自动化工作中非常有用。最后要再次强调技术应当用在正当的领域在获取数据时务必尊重网站的robots.txt规则控制请求速率避免对他人服务造成干扰。