新闻详情

新闻详情

首页 / 资讯中心 / 详情

小红书X-s/X-T签名逆向:JS动态签名原理与Python调用实践

发布时间:2026/9/25 8:16:57来源:尧图网络
小红书X-s/X-T签名逆向:JS动态签名原理与Python调用实践
简介本资源是一份面向前端开发与网络安全学习者的JS逆向实战材料聚焦小红书平台X-s/X-T参数的生成逻辑分析与可运行验证适用于具备JavaScript调试、抓包分析及基础逆向能力的中高级开发者。压缩包共3个文件6KB含HTML可执行页面用于本地调试、.inscode配置文件支持IDE环境集成、.gitignore保障版本管理规范结构精简但功能完整。已有116人学习下载体现了对主流App前端加密机制研究的持续关注。读者可直接运行HTML文件复现X-s参数生成全流程获取包含MD5哈希拼接、x1–x4环境变量提取、a1 Cookie与时间戳动态注入、Base64编码及最终payload构造的完整逆向推演链配套代码清晰标注关键Hook点与插桩日志位置便于理解加密函数调用上下文与环境补全策略。1. 小红书 X-s/X-T 请求头逆向不是“破解”而是理解它怎么防你、又留了哪条缝你写了个爬虫刚跑通小红书搜索页一换用户主页就 403抓了几十个请求发现X-s和X-T这俩请求头像幽灵一样每秒刷新、每次不同用浏览器开发者工具断点跟到sign.js里面全是atob、btoa、String.fromCharCode套娃还夹着Math.random()和Date.now()的影子——这不是玄学是小红书在用 JS 运行时生成动态签名把「你是人」和「你没改请求」这两件事打包成一个可验证的数学承诺。这个标题说的「JS逆向分析小红书X-s/X-T」核心不是绕过风控而是搞懂它怎么构造、怎么校验、怎么随时间/参数/设备指纹变化它不拒绝自动化但拒绝「无上下文的裸请求」。适合三类人想稳定采集公开笔记数据做舆情分析的产品运营、需要批量解析分享链接提取 ID 的内容中台工程师、以及正在被412 Precondition Failed卡住、连curl -v都看不到完整响应头的前端调试者。本文不提供“万能 token”只给你一套可复现、可调试、可嵌入自己 pipeline 的逆向路径——从定位入口函数开始到本地复现签名逻辑再到用 Python 调用真实 JS 上下文生成合法头全程基于 2024 年 Q2 稳定生效的客户端行为非模拟器、非旧版 APK。2. 定位签名生成入口从 Network 面板到 AST 反混淆的三步穿透法小红书 Web 端的签名逻辑藏得不深但故意设了三道门槛资源加载异步化、代码字符串拼接、关键函数名混淆。直接搜X-s或X-T在 Sources 面板里基本找不到——因为它们是运行时拼出来的。必须从请求发起源头倒推。2.1 用 Fetch/XHR 断点锁死调用栈起点打开小红书网页如 https://www.xiaohongshu.com/explore清空 Network 面板执行一次搜索或刷新首页。在 Network 面板顶部点击XHR找到带/api/sns/web/v1/search/notes或/api/sns/web/v1/user_profile的请求。右键 →Break on → Fetch/XHR breakpoint。这时再触发一次相同请求比如回车搜索浏览器会自动在fetch或XMLHttpRequest.send处暂停。提示如果断点没触发检查是否启用了「Disable cache」并关闭所有插件小红书部分接口走POST /api/sns/web/v1/feed优先盯住feed和search类路径。暂停后在 Call Stack 面板里往上翻找最靠近顶层的、文件名含sign、crypto、utils或index的脚本。常见路径是https://cdn-xhs.xiaohongshu.com/static/js/sign.[hash].js或https://cdn-xhs.xiaohongshu.com/static/js/app.[hash].js。点进去你会看到类似这样的调用链// 示例反编译后的调用链非原始代码 function t(e) { const n r(e); // r 是核心签名函数 return { X-s: n.s, X-T: n.t }; }2.2 用 AST 工具解构混淆函数别硬读让机器帮你“翻译”拿到疑似sign.js后不要试图手动还原eval(unescape(...))。用 de4js 或本地javascript-obfuscator的--reverse模式处理。实测 2024 年小红书常用混淆配置为stringArray,identifierNamesGenerator: hexadecimal反混淆后关键函数通常命名为generateSign、getSignature或buildHeader。重点找函数体内同时出现以下特征的代码块Date.now()或new Date()时间戳参与计算Math.random().toString(36).substr(2, 8)随机盐值JSON.stringify()或Object.keys().sort()参数排序序列化CryptoJS.HmacSHA256或window.crypto.subtle.digest哈希算法例如反混淆后你可能看到function generateSign(t, e) { const n Date.now(); const r Math.random().toString(36).substr(2, 8); const i JSON.stringify({ ...e, timestamp: n, rand: r }).replace(//g, ); const s CryptoJS.enc.Utf8.parse(i); const o CryptoJS.enc.Utf8.parse(xiaohongshu_key_2024); const a CryptoJS.HmacSHA256(s, o); const c CryptoJS.enc.Base64.stringify(a); return { s: c, t: n }; }注意xiaohongshu_key_2024是示意密钥实际密钥是硬编码在 JS 中的 Base64 字符串或通过atob()解码得到需继续向下追踪o的来源。2.3 抽取密钥与算法从字符串解码到常量提取密钥往往藏在atob()或btoa()调用链中。在 Sources 面板中全局搜索atob(找到类似atob(YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo)的调用。复制 Base64 字符串到在线解码器如 base64decode.org解出明文密钥。若密钥本身也被混淆如atob(a bc def)则需在 Debugger 中打点鼠标悬停变量查看实时值。实测当前2024.06小红书 Web 端密钥为固定字符串但会随大版本更新轮换。关键结论X-s 是 HmacSHA256 签名X-T 是毫秒级时间戳签名原文 排序后的请求参数 JSON 时间戳 随机字符串密钥是硬编码在 JS 中的 32 字节字符串Base64 编码后约 44 字符。3. 本地复现签名逻辑用 PyExecJS Node.js 环境跑通真实 JS 上下文Python 直接实现 HmacSHA256 不难但小红书签名依赖浏览器环境的CryptoJS、window对象、甚至navigator.userAgent的某些字段。强行用 Python 重写极易翻车——因为 JS 里可能有navigator.platform判断、document.cookie读取、或performance.now()精确计时。最稳方案是让 Python 调用真实 Node.js 环境执行原始 JS 代码。3.1 构建最小可运行 JS 签名模块新建xiaohongshu_sign.js内容如下以 2024.06 实际反混淆逻辑为准// xiaohongshu_sign.js const CryptoJS require(crypto-js); // ⚠️ 此处密钥请替换为你从 JS 中提取的真实密钥Base64 解码后 const SECRET_KEY your_real_secret_key_here; function generateSign(params, timestamp Date.now(), rand Math.random().toString(36).substr(2, 8)) { // 参数排序按 key 字典序升序忽略 undefined/null 值 const sortedKeys Object.keys(params).filter(k params[k] ! undefined params[k] ! null).sort(); const sortedParams {}; for (const k of sortedKeys) { sortedParams[k] params[k]; } sortedParams.timestamp timestamp; sortedParams.rand rand; // 拼接签名原文key1value1key2value2...注意不是 JSON const signStr Object.keys(sortedParams) .map(k ${k}${sortedParams[k]}) .join(); // HmacSHA256 签名 const hash CryptoJS.HmacSHA256(signStr, SECRET_KEY); const signature CryptoJS.enc.Base64.stringify(hash); return { X-s: signature, X-T: timestamp.toString() }; } // 导出供 Python 调用 if (typeof module ! undefined module.exports) { module.exports { generateSign }; }安装依赖npm install crypto-js。测试命令node -e console.log(require(./xiaohongshu_sign).generateSign({keyword:python,page:1}))应输出类似{ X-s: abc123..., X-T: 1717xxxxxx }。3.2 Python 调用 Node.js 执行签名PyExecJS subprocess 双保险PyExecJS在新版 Node.js 下常报错推荐用subprocess直接调用 Node.js。新建sign_client.py# sign_client.py import json import subprocess import sys from pathlib import Path # JS 文件路径绝对路径避免相对路径错误 JS_PATH Path(__file__).parent / xiaohongshu_sign.js def generate_xhs_header(params: dict, node_path: str node) - dict: 调用 Node.js 执行签名 JS返回 X-s/X-T 头 :param params: 请求参数字典如 {keyword: AI, page: 1} :param node_path: node 可执行文件路径默认为系统 PATH 中的 node :return: 包含 X-s 和 X-T 的 dict # 构造调用命令node xiaohongshu_sign.js {keyword:AI,page:1} cmd [ node_path, str(JS_PATH), json.dumps(params, ensure_asciiFalse) ] try: result subprocess.run( cmd, capture_outputTrue, textTrue, timeout5 ) if result.returncode ! 0: raise RuntimeError(fNode.js execution failed: {result.stderr}) # JS 脚本需打印 JSON 到 stdoutPython 解析 header json.loads(result.stdout.strip()) return { X-s: header[X-s], X-T: header[X-T] } except subprocess.TimeoutExpired: raise TimeoutError(Node.js signing timed out) except json.JSONDecodeError as e: raise ValueError(fInvalid JSON from Node.js: {result.stdout}, error: {e}) # 使用示例 if __name__ __main__: headers generate_xhs_header({keyword: 爬虫, page: 1}) print(headers) # {X-s: xxx, X-T: 1717xxxxxx}逻辑说明Python 不解析 JS只传参、调 Node、收 JSON。这样完全规避了 JS 环境差异如window对象缺失、加密库版本不一致、时间精度误差等问题。subprocess比PyExecJS更可控失败时 stderr 清晰可见。3.3 集成到 Requests 请求带 Referer 和 User-Agent 的完整头组装小红书服务端不仅校验X-s/X-T还检查Referer、User-Agent、Cookie尤其是web_session。一个能跑通的最小请求头应包含Header KeyValue 示例说明User-AgentMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36必须匹配你抓包时的 UA否则 403Refererhttps://www.xiaohongshu.com/explore必须是小红书域名下的有效页面X-sxxx由上述函数生成X-T1717xxxxxx与 X-s 生成时的时间戳一致Cookieweb_sessionxxx;从浏览器复制有效期约 2 小时需定期更新import requests def make_xhs_request(url: str, params: dict, cookies: str): headers { User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36..., Referer: https://www.xiaohongshu.com/explore, Cookie: cookies, } # 动态注入 X-s/X-T sign_headers generate_xhs_header(params) headers.update(sign_headers) response requests.get(url, paramsparams, headersheaders, timeout10) if response.status_code 200: return response.json() else: print(fRequest failed: {response.status_code} {response.text[:200]}) return None # 示例调用 cookies web_sessionabc123... # 从浏览器 Application → Cookies 复制 data make_xhs_request( https://www.xiaohongshu.com/api/sns/web/v1/search/notes, {keyword: Python, page: 1, per_page: 20}, cookies )4. 避坑指南X-s/X-T 逆向中最容易踩的 4 个血泪坑逆向小红书签名不是写完就能跑90% 的失败源于环境细节偏差。以下是我在 3 个项目中反复验证的 4 个致命坑每个都附带现象、根因和解法。4.1 现象X-s 签名本地生成正确但服务端校验失败返回 412原因签名原文拼接方式错误。小红书不是用JSON.stringify()而是用key1value1key2value2的 URL 查询字符串格式且要求参数 key 按字典序升序排列value 不做 URL 编码注意不是encodeURIComponent。若你用了urllib.parse.urlencode()它会对和编码导致签名原文不一致。解决严格按Object.keys().sort().map(k ${k}${params[k]}).join()拼接value 保持原始字符串如keywordAI不是keywordAI%20Tools。4.2 现象X-T 时间戳正确但 X-s 每次都变且服务端返回invalid signature原因rand随机字符串生成逻辑不一致。JS 中Math.random().toString(36).substr(2, 8)生成 8 位 36 进制字符串Python 的random.choices(string.ascii_letters string.digits, k8)生成的是 62 进制且random模块种子不可控。更糟的是有些 JS 版本用crypto.getRandomValues(new Uint8Array(4))生成真随机数。解决永远让 Node.js 生成 rand。修改xiaohongshu_sign.js把rand参数移入函数内由 JS 自己生成const rand Math.random().toString(36).substr(2, 8);。Python 不传 rand只传params和timestamp可选。4.3 现象同一套代码昨天能跑今天 403X-s/X-T 值看起来“合法”原因密钥轮换。小红书每 2~4 周会更新 JS 中的密钥旧密钥失效。你本地 JS 文件没更新但线上已切新密钥。解决建立密钥监控机制。每周用 Puppeteer 自动访问首页提取最新sign.js正则匹配atob\(.*?\)解码比对。一旦发现变化人工确认后更新SECRET_KEY。不要依赖“永久密钥”。4.4 现象请求成功返回数据但后续请求如详情页403X-s/X-T 明明是刚生成的原因web_sessionCookie 过期或不匹配。X-s/X-T 签名虽正确但服务端会关联web_session的设备指纹如navigator.userAgentscreen.widthlanguage。若你用 Requests 发请求web_session是从 Chrome 复制的但 UA 是 Python 默认 UA设备指纹不一致触发风控。解决Headers 中的User-Agent必须与你获取web_session时的浏览器 UA 完全一致。用navigator.userAgent复制不要手写简写。同时Referer必须是该web_session对应的有效页面如https://www.xiaohongshu.com/user/profile/xxx。5. 进阶技巧用 Puppeteer 自动化密钥提取与 Session 维护手动复制web_session和密钥不可持续。真正的生产级方案是让浏览器自动化完成「登录 → 提取 session → 提取密钥 → 生成签名」全链路。Puppeteer 是目前最稳的选择——它启动真实 Chromium执行 JS抓取 DOM还能拦截网络请求。5.1 自动提取最新密钥从 Network 面板抓sign.js内容// extract_key.js const puppeteer require(puppeteer); async function extractSecretKey() { const browser await puppeteer.launch({ headless: true }); const page await browser.newPage(); // 设置 UA 避免被拦截 await page.setUserAgent(Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36...); // 访问首页触发 sign.js 加载 await page.goto(https://www.xiaohongshu.com/, { waitUntil: networkidle2 }); // 监听所有 JS 请求过滤 sign.js let signJsContent ; page.on(response, async response { const url response.url(); if (url.includes(sign.) url.endsWith(.js)) { signJsContent await response.text(); } }); // 等待 sign.js 加载完成最多 10 秒 await page.waitForTimeout(10000); // 用正则提取密钥匹配 atob(...) const keyMatch signJsContent.match(/atob\(([^])\)/); if (keyMatch) { const base64Key keyMatch[1]; const secretKey Buffer.from(base64Key, base64).toString(utf8); console.log(Extracted secret key:, secretKey); return secretKey; } else { throw new Error(Failed to extract secret key from sign.js); } } extractSecretKey();运行node extract_key.js输出即为最新密钥。5.2 自动维护 web_session用 Puppeteer 登录并导出 Cookie小红书 Web 端登录需短信验证码无法全自动。但可半自动用 Puppeteer 打开登录页人工输入验证码然后导出web_session。// maintain_session.js const puppeteer require(puppeteer); async function exportSession() { const browser await puppeteer.launch({ headless: false }); // 非无头方便人工操作 const page await browser.newPage(); await page.goto(https://www.xiaohongshu.com/login); // 等待用户手动登录最多 5 分钟 await page.waitForFunction(() document.cookie.includes(web_session), { timeout: 300000 }); // 获取所有 Cookie const cookies await page.cookies(); const webSession cookies.find(c c.name web_session); if (webSession) { console.log(web_session:, webSession.value); // 可写入文件供 Python 读取 require(fs).writeFileSync(session.txt, webSession.value); } } exportSession();运行后浏览器弹出登录页你输入手机号和验证码登录成功后脚本自动提取web_session并保存。5.3 统一调度Python 主流程串联签名、Session、请求最终把所有环节串成一个xhs_api.py# xhs_api.py import json import subprocess import time from pathlib import Path class XiaoHongShuAPI: def __init__(self, node_pathnode): self.node_path node_path self.session_file Path(session.txt) self.secret_key_file Path(secret_key.txt) def _get_session(self) - str: if not self.session_file.exists(): raise FileNotFoundError(session.txt not found. Run maintain_session.js first.) return self.session_file.read_text().strip() def _get_secret_key(self) - str: if not self.secret_key_file.exists(): raise FileNotFoundError(secret_key.txt not found. Run extract_key.js first.) return self.secret_key_file.read_text().strip() def _generate_header(self, params: dict) - dict: # 更新 JS 文件中的密钥可选每次调用前写入最新密钥 with open(xiaohongshu_sign.js, r) as f: js_content f.read() new_js js_content.replace( const SECRET_KEY your_real_secret_key_here;, fconst SECRET_KEY {self._get_secret_key()}; ) with open(xiaohongshu_sign.js, w) as f: f.write(new_js) # 调用 Node 生成头 cmd [self.node_path, xiaohongshu_sign.js, json.dumps(params)] result subprocess.run(cmd, capture_outputTrue, textTrue, timeout5) return json.loads(result.stdout.strip()) def search_notes(self, keyword: str, page: int 1): headers { User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36..., Referer: https://www.xiaohongshu.com/explore, Cookie: fweb_session{self._get_session()}; } headers.update(self._generate_header({keyword: keyword, page: page})) response requests.get( https://www.xiaohongshu.com/api/sns/web/v1/search/notes, params{keyword: keyword, page: page, per_page: 20}, headersheaders, timeout10 ) return response.json() # 使用 api XiaoHongShuAPI() data api.search_notes(Python, page1) print(json.dumps(data, indent2, ensure_asciiFalse))这套方案把「密钥更新」、「Session 维护」、「签名生成」、「请求发送」全部封装只需定期运行extract_key.js和maintain_session.js其余全自动。它不追求 100% 免人工但把人工操作压缩到最低频次密钥每月一次Session 每 2 小时一次而日常请求完全无人值守。我坚持用 Puppeteer 而不是 Selenium因为它的 network interception 更精准headless 模式更轻量且对小红书这种重度 JS 渲染站点兼容性更好。曾经试过用 Playwright但它在atob解码环节偶发乱码Puppeteer 稳定得多。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

谷歌把 TPU 送上了天:4 颗芯片、15 分钟,太空数据中心的第一次真刀真枪 2026/9/25 8:54:22

谷歌把 TPU 送上了天:4 颗芯片、15 分钟,太空数据中心的第一次真刀真枪

💡 一句话总结:谷歌的太空 AI 算力计划 Project Suncatcher 从纸面论文走进了发射场——首颗原型卫星定档 10 月 1 日,但只带 4 颗 TPU、每次跑 15 分钟;愿景(81 星组网)与现状(一次 15 分钟的验…

阅读更多 →
影刀RPA实战:微信聊天记录自动导出Excel的完整方案 2026/9/25 8:54:09

影刀RPA实战:微信聊天记录自动导出Excel的完整方案

做运营的人应该都经历过这种场景:领导说“把上个月和A客户的所有聊天记录整理成表格”,你只能打开微信,一条条往上翻,复制粘贴到Excel里,再手工标记日期和联系人。聊天少还好,遇到一天几十条的群&#xff0…

阅读更多 →
PaddleSpeech 语音特征提取实战:解析 python_kaldi_features 的 MFCC、Fbank 实现与 Kaldi 对齐细节 2026/9/25 8:54:09

PaddleSpeech 语音特征提取实战:解析 python_kaldi_features 的 MFCC、Fbank 实现与 Kaldi 对齐细节

人工智能语音音频 【免费下载链接】PaddleSpeech Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation and Keyword…

阅读更多 →
Java毕设实战:基于SpringBoot+SSM的蛋糕购物平台系统解析 2026/9/25 8:53:49

Java毕设实战:基于SpringBoot+SSM的蛋糕购物平台系统解析

很多Java学习者第一次真正接触到“一个完整系统”,就是从做这类商城项目开始的。云与糖蛋糕购物平台系统就是这样一个很典型的JavaSpringBootSSM项目:用户端能注册登录、按分类浏览蛋糕、把心仪的甜品加入购物车、下单模拟支付;管理端能维护商…

阅读更多 →
Java变量深度解析:内存模型、作用域、常量与命名规范 2026/9/25 8:53:49

Java变量深度解析:内存模型、作用域、常量与命名规范

变量大概是Java里第一个绕不开、又被大多数教程一句话带过的概念。我见过工作两三年的开发,能把集合框架、JVM调优聊得头头是道,但你问他int a 10;这一行到底发生了什么,他反而含糊其辞。变量看起来简单,简单到我们每天都在写&am…

阅读更多 →
Tekton Pipeline 依赖库 go-fed/httpsig:HTTP Signatures 请求/响应签名与验证实现解析 2026/9/25 8:53:43

Tekton Pipeline 依赖库 go-fed/httpsig:HTTP Signatures 请求/响应签名与验证实现解析

云原生CI/CDDevOps后端 【免费下载链接】pipeline A cloud-native Pipeline resource. 项目地址: https://gitcode.com/gh_mirrors/pipelin/pipeline 点击查看 免费下载 本文以 Tekton Pipeline 仓库中 vendored 的第三方库 go-fed/httpsig(v1.1.0&…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉