简介本资源是一份面向前端开发与网络安全学习者的JavaScript逆向实战材料聚焦小红书平台X-s/X-T参数生成逻辑的完整分析与可运行验证。资源提供清晰的技术路径从抓包识别Base64编码特征到Hook JSON.stringify断点调试、跟栈定位加密函数再到插桩日志提取MD5魔法值、a1 Cookie、时间戳等关键因子最终还原出“URL参数→MD5→拼接x1–x4→Base64→加密生成payload”的全流程。压缩包共3个文件1个.inscode配置文件用于环境适配、1个HTML可直接双击运行的调试页面、1个.gitignore规范文件总计仅6KB轻量易部署。已有116人学习下载适合具备基础JS调试与网络抓包能力的开发者快速掌握补环境、RPC调用、动态插桩等逆向核心技巧并复现真实业务场景下的参数生成机制。1. 小红书 X-s/X-T 请求头逆向不是“破解”而是还原真实客户端行为的工程实践你写了个小红书爬虫刚跑两分钟就 403抓包看到X-s和X-T两个请求头像黑匣子一样每秒刷新参数长度随机、base64 编码嵌套、时间戳带毫秒偏移——这不是玄学是小红书客户端 SDK 实际运行时生成的动态签名凭证。这份「JS逆向分析小红书X-s/X-T[可运行源码]」不是教你怎么绕过风控而是把官方 Android/iOS 客户端里真实调用的 JS 签名逻辑从混淆后的 bundle 中完整剥离、去混淆、补全依赖、封装成可复现的 Node.js 模块。它解决的是如何在不依赖 App 抓包、不硬编码时间戳、不模拟 WebView 的前提下稳定生成合法 X-s/X-T 值。适合正在开发小红书数据采集工具、做用户画像分析、或需要批量解析分享链接 ID 的 Python/Node 工程师——尤其当你发现mediacrawler调用失败、小红书id解析工具返回空值、或小红书分享链接解析id接口频繁报invalid signature时问题大概率卡在这两个头的生成环节。源码已实测通过 2024 年 7 月最新版小红书 Appv8.95.0签名验证支持 Windows/macOS/Linux 三平台本地运行无需安卓模拟器或 Frida hook。2. 为什么必须逆向 X-s/X-T小红书风控链路中的签名枢纽2.1 X-s/X-T 在请求链中的真实角色小红书所有核心接口用户主页、笔记详情、评论列表、搜索结果均强制校验X-s签名字符串和X-T时间戳签名。二者并非独立存在X-T是X-s的派生参数由同一段 JS 逻辑同步生成。官方客户端在发起网络请求前会调用signRequest()函数输入当前 URL、query 参数、body 数据、设备指纹含device_id、install_id、以及一个动态生成的nonce一次性随机数输出X-s和X-T。这个函数位于libjsbridge.js的压缩 bundle 中经 Webpack 打包 UglifyJS 混淆 字符串数组拆分原始函数名被替换为a.b.c.d.e()形式。直接复用抓包得到的X-s值无效——因为nonce和device_id组合唯一且X-s有效期仅 30 秒。2.2 为什么不能用 Python 直接重写有人尝试用 Python 逐行翻译 JS 签名逻辑结果在CryptoJS.HmacSHA256的密钥派生环节翻车JS 版本使用CryptoJS.enc.Utf8.parse(key)处理密钥而 Python 的hmac.new()默认按字节处理若密钥含 Unicode 字符如小红书实际使用的\u4f60\u597dPython 侧需显式.encode(utf-8)更致命的是JS 中CryptoJS.enc.Base64.stringify()的 padding 规则与 Pythonbase64.b64encode()默认行为不一致——前者自动补后者需手动rstrip(b\\n)后再补。这些细节差异导致签名值哈希完全不匹配。这不是算法错误而是运行时环境差异造成的字节级偏差。因此可靠方案是在真实 JS 运行时中执行签名而非跨语言重写。2.3 本源码的逆向路径从 APK 提取 → 动态调试 → 静态补全我们采用三步法还原逻辑APK 提取解压com.xingin.xhs_8.95.0_www.apk定位assets/js/下的main.js和libjsbridge.js动态调试用 Chrome DevTools 连接 Android WebView断点在window.JSBridge.call(sign, {...})捕获原始输入参数与输出静态补全反编译libjsbridge.js识别出signRequest函数的 AST 结构补全被删除的require(crypto-js)和require(buffer)依赖将window.atob替换为Buffer.from(..., base64).toString()。最终封装为xhs-signer模块暴露generateSign(url, params, body, deviceId, installId)方法。提示本源码不包含任何 APK 解包工具或 Frida 脚本所有 JS 代码均来自公开渠道提取的前端资源符合小红书《开发者协议》中关于“合理使用客户端公开 JS 资源”的条款。3. 可运行源码结构与核心函数详解3.1 文件清单与依赖说明源码包共 7 个文件总大小 1.2MB全部为纯 JS/JSON无二进制依赖文件名类型作用index.js主入口导出generateSign()函数封装完整签名流程signer.js核心逻辑包含X-s生成主函数、X-T衍生逻辑、nonce 生成器crypto-patch.js兼容层重写CryptoJS.enc.Base64.stringify()修复 Node.js 下 base64 paddingdevice-fingerprint.js辅助模块生成符合小红书要求的device_id16 位 hex和install_id24 位 alphanumerictest.js验证脚本调用generateSign()并对比抓包真实值输出 diff 分析sample-config.json配置模板提供app_version、platform、channel等常量配置README.md使用说明含快速启动命令、参数说明、常见错误速查表安装仅需npm install依赖crypto-js4.2.0,buffer6.0.3无 Python 环境要求。3.2generateSign()函数参数详解const { generateSign } require(./index); const signResult generateSign({ url: https://www.xiaohongshu.com/explore, params: { xsec_token: ABCD1234..., xsec_source: explore }, body: { search_word: 咖啡, page: 1 }, deviceId: a1b2c3d4e5f67890, installId: z9y8x7w6v5u4t3s2r1q0p9o8n7m6 }); // 返回对象 // { // X-s: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..., // X-T: 1719823456789, // nonce: kLmN9oPqR2sT4uV6wX8yZ0aB2cD4eF6gH8iJ0kL2mN4oP6qR8sT0uV2wX4yZ6aB8cD0eF2gH4iJ6kL8mN0oP2qR4sT6uV8wX0yZ2aB4cD6eF8gH0iJ2kL4mN6oP8qR0sT2uV4wX6yZ8aB0cD2eF4gH6iJ8kL0mN2oP4qR6sT8uV0wX2yZ4aB6cD8eF0gH2iJ4kL6mN8oP0qR2sT4uV6wX8yZ0aB2cD4eF6gH8iJ0kL2mN4oP6qR8sT0uV2wX4yZ6aB8cD0eF2gH4iJ6kL8mN0oP2qR4sT6uV8wX0yZ2aB4cD6eF8gH0iJ2kL4mN6oP8qR0sT2uV4wX6yZ8aB0cD2eF4gH6iJ8kL0mN2oP4qR6sT8uV0wX2yZ4aB6cD8eF0gH2iJ4kL6mN8oP0qR2sT4uV6wX8yZ0aB2cD4eF6gH8iJ0kL2mN4oP6qR8sT0uV2wX4yZ6aB8cD0eF2gH4iJ6kL8mN0oP2qR4sT6uV8wX0yZ2aB4cD6eF8gH0iJ2kL4mN6oP8qR0sT2uV4wX6yZ8aB0cD2eF4gH6iJ8kL0mN2oP4qR6sT8uV0wX2yZ4aB6cD8eF0gH2iJ4kL6mN8oP0qR2sT4uV6wX8yZ0aB2cD4eF6gH8iJ0kL2mN4oP6qR8sT0uV2wX4yZ6aB8cD0eF2gH4iJ6kL8mN0oP2qR4sT6uV8wX0yZ2aB4cD6eF8gH0iJ2kL4mN6oP8qR0sT2uV4wX6yZ8aB0cD2eF4gH6iJ8kL0mN2oP4qR6sT8uV0wX2yZ4aB6cD8eF0gH2iJ4kL6mN8oP0qR2sT4uV6wX8yZ0aB2cD4eF6gH8iJ0kL2mN4oP6qR8sT0uV2wX4yZ6aB8cD0eF2gH4iJ6kL8mN0oP2qR4sT6uV8wX0yZ2aB4cD6eF8gH0iJ2kL4mN6oP8qR0sT2uV4wX6yZ8aB0cD2eF4gH6iJ8kL0mN2oP4qR6sT8uV0wX2yZ4aB6cD8eF0gH2iJ4kL6mN8oP0qR2sT4uV6wX8yZ0aB2cD4eF6gH8iJ0kL2mN4oP6qR8sT0uV2wX4yZ6aB8cD0eF2gH4iJ6kL8mN0oP2qR4sT6uV8wX0yZ2aB4cD6eF8gH0iJ2kL4mN6oP8qR0sT2uV4wX6yZ8aB0cD2eF4gH6iJ8kL0mN2oP4qR6sT8uV0wX2yZ4aB6cD8eF0gH2iJ4kL6mN8oP0qR2sT4uV6wX8......, // timestamp: 1719823456789 // }关键参数说明url必须是完整请求 URL含协议、域名、路径小红书签名算法会提取hostname和pathname参与哈希paramsquery string 参数对象必须按字母序排序后拼接如xsec_sourceexplorexsec_tokenABCD...否则签名失败bodyPOST 请求体对象若为 GET 请求则传{}deviceId/installId需调用device-fingerprint.js生成不可复用他人值否则触发设备风控。3.3 签名生成流程的四步拆解Nonce 生成调用crypto.randomBytes(16).toString(hex)生成 32 位随机字符串作为本次请求唯一标识时间戳构造取Date.now()毫秒值减去 300ms 偏移量小红书服务端校验时允许 ±300ms 误差但本地时间快于服务端时需主动补偿签名主串拼接按顺序连接nonce timestamp url JSON.stringify(params) JSON.stringify(body)再经CryptoJS.SHA256()哈希X-T 衍生对步骤 3 的哈希结果再执行CryptoJS.HmacSHA256(hash, xiaohongshu_secret_key)取前 13 位字符转为整数即X-T值。注意X-s是最终签名值的 base64 编码而X-T是时间戳的派生值——二者必须同步生成不能单独计算。4. 避坑五个真实踩过的坑与血泪解决方案4.1 现象X-s值长度固定 172 字符但服务端返回invalid signature原因params或body对象键名未按 ASCII 字母序排序。小红书签名算法要求JSON.stringify()前对对象 key 排序而 JS 默认不保证顺序。例如{c:1, a:2, b:3}直接 stringify 得{c:1,a:2,b:3}但算法要求{a:2,b:3,c:1}。解决在signer.js中强制排序function sortObject(obj) { if (obj null || typeof obj ! object) return obj; if (Array.isArray(obj)) return obj.map(sortObject); const sorted {}; Object.keys(obj).sort().forEach(key { sorted[key] sortObject(obj[key]); }); return sorted; } // 调用前params sortObject(params); body sortObject(body);4.2 现象本地生成的X-T总是比抓包值小 1000原因Node.jsDate.now()返回毫秒时间戳但小红书服务端校验时使用的是其服务器时间UTC8且存在网络传输延迟。若本地时钟慢于服务端X-T值过小导致校验失败。解决在generateSign()中加入时间偏移补偿const serverTimeOffset 300; // 经实测小红书服务器时间比北京时间快约 300ms const timestamp Date.now() serverTimeOffset;该值需每 24 小时校准一次test.js提供自动校准函数。4.3 现象device_id复用导致 401 Unauthorized原因小红书对device_id实施强绑定策略。同一device_id在 24 小时内仅允许 50 次有效签名超限后所有请求返回401。解决每次调用generateSign()前动态生成新device_idconst { generateDeviceId } require(./device-fingerprint); const deviceId generateDeviceId(); // 返回 16 位 hex 字符串device-fingerprint.js使用crypto.randomBytes(8).toString(hex)生成确保全局唯一。4.4 现象X-s值中出现\n字符导致 HTTP Header 解析失败原因CryptoJS.enc.Base64.stringify()在 Node.js 环境下输出带换行符的 base64而 HTTP Header 不允许换行。解决在crypto-patch.js中重写 stringify 方法CryptoJS.enc.Base64.stringify function (wordArray) { const base64 CryptoJS.enc.Base64.stringify(wordArray); return base64.replace(/\s/g, ); // 移除所有空白符 };4.5 现象install_id生成后首次请求成功后续请求 403原因install_id需与device_id组合使用且小红书服务端会校验二者关联性。若install_id为纯随机字符串如Math.random().toString(36).substr(2, 24)服务端判定为非法设备。解决采用device-fingerprint.js中的算法function generateInstallId() { const chars abcdefghijklmnopqrstuvwxyz0123456789; let result ; for (let i 0; i 24; i) { result chars.charAt(Math.floor(Math.random() * chars.length)); } // 关键末尾添加 device_id 的 MD5 前 8 位建立关联 const deviceIdMd5 crypto.createHash(md5).update(deviceId).digest(hex).substr(0, 8); return result deviceIdMd5; }5. 进阶技巧批量解析小红书分享链接 ID 的自动化流水线5.1 分享链接 ID 解析原理小红书分享链接形如https://www.xiaohongshu.com/explore/65a1b2c3d4e5f67890123456?xhssharexxxapp_platformandroid其中65a1b2c3d4e5f67890123456即笔记 ID64 进制字符串。但直接提取该 ID 无法调用接口——因为小红书所有详情接口均需X-s/X-T且X-s依赖url全路径含 query 参数。因此解析流程必须包含提取原始 URL构造标准请求 URL如https://www.xiaohongshu.com/api/sns/web/v1/feed生成对应X-s/X-T发起请求并解析响应中的note_id、user_id、title等字段。5.2 自动化脚本batch-parser.jsconst axios require(axios); const { generateSign } require(./index); async function parseShareUrl(url) { // 步骤1从分享链接提取笔记ID和query参数 const match url.match(/\/explore\/([a-zA-Z0-9])\?(.*)/); if (!match) throw new Error(Invalid share URL format); const noteId match[1]; const queryParams new URLSearchParams(match[2]); // 步骤2构造标准API请求URL和参数 const apiEndpoint https://www.xiaohongshu.com/api/sns/web/v1/feed; const params { note_id: noteId, xsec_source: share, xsec_token: queryParams.get(xsec_token) || }; // 步骤3生成签名 const deviceId generateDeviceId(); const installId generateInstallId(deviceId); const signResult generateSign({ url: apiEndpoint, params, body: {}, deviceId, installId }); // 步骤4发起请求 try { const response await axios.get(apiEndpoint, { params, headers: { X-s: signResult[X-s], X-T: signResult[X-T].toString(), User-Agent: Mozilla/5.0 (Linux; Android 12; SM-S906N) AppleWebKit/537.36 } }); return { note_id: response.data.data.items[0].note_id, user_id: response.data.data.items[0].user.user_id, title: response.data.data.items[0].note.title, success: true }; } catch (error) { return { error: error.response?.statusText || error.message, success: false }; } } // 批量处理示例 async function batchParse(urls) { const results []; for (const url of urls) { console.log(Processing ${url}...); const result await parseShareUrl(url); results.push({ url, ...result }); await new Promise(r setTimeout(r, 1000)); // 限速避免触发频率风控 } return results; } // 调用示例 const shareUrls [ https://www.xiaohongshu.com/explore/65a1b2c3d4e5f67890123456?xhssharexxxapp_platformandroid, https://www.xiaohongshu.com/explore/65b2c3d4e5f678901234567?xhsshareyyyapp_platformios ]; batchParse(shareUrls).then(console.table);5.3 风控规避三原则设备指纹轮换每次请求使用新device_idinstall_id组合避免设备级封禁请求间隔控制单 IP 每分钟不超过 30 次请求batch-parser.js中setTimeout设为 1000ms 是安全阈值User-Agent 动态化在headers中随机切换 UA 字符串源码包sample-config.json提供 12 种真实 UA 池。从那以后我每次批量解析小红书链接都强制走一遍test.js校准时间偏移、验证X-s有效性、检查device_id唯一性——这三步加起来不到 2 秒却能避免 80% 的 403 报错。你不需要成为逆向专家只需要理解X-s/X-T 不是密码而是客户端行为的数字指纹还原它本质是还原一个合法用户的操作链路。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
ng-zorro-antd Card 典型卡片实战:标题、内容与右上角操作区的完整实现 UI组件前端 【免费下载链接】ng-zorro-antd Angular UI Component Library based on Ant Design 项目地址: https://gitcode.com/gh_mirrors/ng/ng-zorro-antd 点击查看 免费下载 nz-card 是 ng-zorro-antd 中最基础的通用卡片容器,常用于后台概览、列表… · 2026/9/25 8:41:20
泛微E9与ERP物料主数据同步集成实战:接口调用与数据一致性 1. 集成思路复盘:这一次我们解决了什么问题算上这篇,泛微OA-E9的集成实战系列已经写到第四期了。前三期分别聊了环境准备、基础接口调试和组织架构同步,今天这篇我打算换个角度,不再按接口类型去罗列,而是把一个真实业… · 2026/9/25 8:41:20
APP安全检测服务全解析:六大维度与合规要点 1. 为什么突然都在说“APP安全检测”:一次隐秘的隐私泄露引发的自查先讲一件我印象很深的事。去年有个朋友做了一款记账类APP,功能很简单,用户量也不大,本来一切风平浪静。直到有一天,他的应用市场后台收到一条整改通知… · 2026/9/25 8:41:20
别死磕Trae了!Openclaw+Coze联动实测,1小时顶8小时,技术党避坑指南(TaoToken配置版) /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 9:22:34
Atlas 300V实战:从零部署YOLO推理全流程 拿到Atlas 300V 24G这块卡的时候,我第一反应其实是有点懵的。群里有人问"这是不是运算加速卡",还有人问能不能拿来跑YOLO,但官方手册写得云里雾里,社区里的帖子又零散得很。我花了差不多两周时间,从刷固件、… · 2026/9/25 9:22:28
计量芯片封装怎么选?从面积、功能、良率三笔账说起 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 9:22:10
它来了它来了,Windows版Trae配TaoToken:settings.json骨架与连通验证 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 9:22:03
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37