xiaohongshu-xs-reverse

⚠️ Disclaimer: This article is only used for security research and technical exchanges. Please do not use related technologies for illegal purposes. All consequences caused by malicious use of related technologies shall be borne by the users themselves.

Target site: https://www.xiaohongshu.com/explore

Overview

The Xiaohongshu web client carries theX-SRequest header for signature verification. Simply put, the server will verify the request headerX-SIs it legal? If not, the request will be rejected directly. This article will completely record the analysis process of this signature mechanism, from environment simulation to algorithm restoration, to using Python calls to generate signatures and initiate requests, taking you step by step to achieve automatic generation.X-S

Reverse entrance positioning

To find the signature logic, the first step is to locate the encryption function in the code. Open the target site and follow these steps:

  1. Enter the Xiaohongshu exploration page, open the browser developer tools (F12), and switch to the Network panel.
  2. FilterXHR/FetchRequest, just scroll the page to trigger some loading, find withX-SRequest header interface, such as home page feed interface/api/sns/web/v1/homefeed
  3. In the Sources panel, search globallyX-sorX-SStrings are generally found in some request interceptors where they are assigned values.
  4. A more direct method is to useXHR/fetch Breakpoints: Add a filter condition in XHR/fetch Breakpoints on the right side of the Sources panel, such as/api/sns/web/v1/homefeed, so that it will be automatically disconnected before the request is sent. Then trace back all the way up the call stack to find the logic for signature generation.

💡 Tips: If you searchX-SThere are too many strings, you can try searchingXYS_The prefix, which is the fixed beginning of the final signature, has a high probability of directly locating the core encryption function.

After tracking and analysis, it will be found that the signature logic is roughly hidden in a huge Webpack packaging file, and the function names are usually obfuscated. All we need to do is strip out the key encryption logic and reproduce it in the Node.js environment.

Core signature process analysis

1. Signature generation steps

Through breakpoint debugging and code deobfuscation, sort outX-SThe complete generation process:

  1. Splicing string: Convert the API path (such as/api/sns/web/v1/homefeed) concatenated with the JSON string of request parameters.
  2. Calculate MD5: Get the MD5 digest of the string obtained in the previous step.
  3. Call mnsv2 algorithm: Pass the original concatenated string and MD5 value into a file namedmnsv2Custom function to generate core signature (signature)。
  4. Assemble signature object: Combine the version number, platform, operating system, signature obtained in the previous step, etc. into a fixed-structure object.
  5. Encoding output: Convert the signature object to a JSON string, encode it with UTF-8 and customize Base64 encoding, and finally addXYS_prefix, get the finalX-Svalue.

2. Key data structures

The final object structure involved in coding is roughly as follows:

const signObj = {
    x0: "4.2.1",        // API 版本号,需与当前页面对应,可能会更新
    x1: "xhs-pc-web",   // 平台固定标识
    x2: "Windows",      // 操作系统信息,也可以是 macOS 等
    x3: signature,      // mnsv2 算法生成的核心签名
    x4: 'object'        // 固定字段
};

The entire signature process can be summarized in one sentence: "The parameters are put together to calculate MD5, then thrown to mnsv2 to generate the core signature, and finally packaged with custom Base64."

Key code implementation

1. Browser environment simulation

Xiaohongshu’s front-end code will verify the code running environment (such as checkingnavigatorproperties), if we want to implement pure algorithms in Node.js, we must complete these global objects in advance. Through means such as Proxy and class inheritance, the original code can be "cheated" so that it can still run normally in the Node environment.

// 全局对象代理与模拟
window = global;
self = window;
globalThis = self;

// 以下浏览器核心类需要根据被调用的属性逐步补全
Navigator = function() {};
Navigator.prototype = { userAgent: "Mozilla/5.0 ...", platform: "Win32" };
navigator = new Navigator();

Location = function() {};
Location.prototype = { href: "https://www.xiaohongshu.com" };
location = new Location();

// 其他对象如 screen、document 等也可以如法炮制
screen = { width: 1920, height: 1080, colorDepth: 24 };

NOTE:mnsv2The specific environmental attributes used in the function need to be supplemented based on your reverse results, and are not fixed. The code above is just a sample framework.

2. Core signature function

With the patched environment in place, you can run the stripped encryption code. The following is a simplified main logic example. In actual use, you need to reverse themnsv2The function is completely moved over.

const CryptoJS = require('crypto-js');

function generateXSign(apiPath, params) {
    // 1. 拼接 API 路径与参数 JSON
    const paramStr = apiPath + JSON.stringify(params);

    // 2. 计算拼接字符串的 MD5
    const md5Hash = CryptoJS.MD5(paramStr).toString();

    // 3. 调用 mnsv2 算法生成核心签名(需要你逆向得到的 mnsv2 实现)
    const signature = window.mnsv2(paramStr, md5Hash);

    // 4. 组装最终对象并编码
    const signObj = {
        x0: "4.2.1",
        x1: "xhs-pc-web",
        x2: "Windows",
        x3: signature,
        x4: 'object'
    };

    const signJson = JSON.stringify(signObj);
    const utf8Bytes = encodeUtf8(signJson);
    const base64Str = b64Encode(utf8Bytes);

    return "XYS_" + base64Str;
}

3. Auxiliary encoding function

The Base64 used by Xiaohongshu is a custom dictionary, not the standard Base64. The corresponding character table needs to be extracted during reverse engineering. The following is an example written according to a common pattern. Pay attention to thekeyStrReplace it with the specific string you reversed.

// UTF-8 编码
function encodeUtf8(str) {
    return unescape(encodeURIComponent(str));
}

// 自定义 Base64 编码(注意替换正确字典)
function b64Encode(input) {
    // 此字典仅供参考,实际值需要从逆向代码中扣出来
    const keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    let output = "";
    let i = 0;
    while (i < input.length) {
        const char1 = input.charCodeAt(i++);
        const char2 = input.charCodeAt(i++);
        const char3 = input.charCodeAt(i++);
        const enc1 = char1 >> 2;
        const enc2 = ((char1 & 3) << 4) | (char2 >> 4);
        const enc3 = ((char2 & 15) << 2) | (char3 >> 6);
        const enc4 = char3 & 63;

        if (isNaN(char2)) {
            enc3 = enc4 = 64;
        } else if (isNaN(char3)) {
            enc4 = 64;
        }
        output += keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
    }
    return output;
}

End-to-end call example

In order to facilitate actual use, we can encapsulate the Node.js script into a signature service, then use Python to call the script to obtain the signature, and then carry the signature to request the interface.

1. Node.js signature script (xiaohongshu_sign.js)

Complete the above environment code,mnsv2Functions and generated functions are integrated into one file, and the output is in JSON format at the end.X-s

// ... 省略环境模拟和 mnsv2 函数引入 ...

const apiPath = '/api/sns/web/v1/homefeed';
const params = {
    cursor_score: "",
    num: 31,
    refresh_type: 1,
    note_index: 10,
    unread_begin_note_id: "",
    unread_end_note_id: "",
    unread_note_count: 0,
    category: "homefeed.fashion_v3",
    search_key: "",
    need_num: 6,
    image_formats: ["jpg", "webp", "avif"],
    need_filter_image: false,
};

const x_s = generateXSign(apiPath, params);
console.log(JSON.stringify({ "X-s": x_s }));

2. Python calling script

passsubprocessCall Node.js, get the return value and initiate the request. Don't forget to fill in your own cookies at the same time.

import requests
import subprocess
import json
import time

# 替换成你自己的 Cookie,注意定期更新
cookies = {
    'a1': '你的a1值',
    'web_session': '你的web_session值',
}

# 调用 Node.js 获取签名
result = subprocess.run(
    ['node', 'xiaohongshu_sign.js'],
    capture_output=True,
    text=True,
    encoding='utf-8'
)
output = result.stdout
sign_data = json.loads(output)
x_s_value = sign_data["X-s"]
print(f"生成的 X-S: {x_s_value}")

# 构造请求头
headers = {
    'accept': 'application/json, text/plain, */*',
    'content-type': 'application/json;charset=UTF-8',
    'origin': 'https://www.xiaohongshu.com',
    'referer': 'https://www.xiaohongshu.com/',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    'x-s': x_s_value,
    'x-s-common': '你的x-s-common值',
    'x-t': str(int(time.time() * 1000)),
}

# 请求体
json_data = {
    'cursor_score': '',
    'num': 31,
    'refresh_type': 1,
    'note_index': 10,
    'unread_begin_note_id': '',
    'unread_end_note_id': '',
    'unread_note_count': 0,
    'category': 'homefeed.fashion_v3',
    'search_key': '',
    'need_num': 6,
    'image_formats': ['jpg', 'webp', 'avif'],
    'need_filter_image': False,
}

# 发送请求
resp = requests.post(
    'https://edith.xiaohongshu.com/api/sns/web/v1/homefeed',
    cookies=cookies,
    headers=headers,
    json=json_data
)

data = resp.json()
if data.get('success'):
    for item in data['data']['items']:
        print(item)
else:
    print(f"请求失败: {data}")

Key Notes

  1. Environment simulation must be accurate:mnsv2Generally, the algorithm will accessnavigator.userAgentscreen.widthand other attributes, missing any one will lead to signature calculation errors.
  2. JSON serialization order:JSON.stringifyThe output results for key-value pairs in different orders may be different, so be sure to ensure that they are completely consistent with the browser. Usually Hook can be used when reversingJSON.stringifyto observe the original parameters.
  3. Base64 custom dictionary: Do not use Node.js nativeBuffer.toString('base64'), must be implemented using a reverse-engineered custom dictionary.
  4. Version number and algorithm update:x0Version,mnsv2Functions may change as the site is upgraded, and long-term maintenance needs to be prepared.

Summarize

Xiaohongshu Web versionX-SSignatures increase the threshold for constructing requests through a combination of "environmental verification + multi-layer encoding + custom algorithms". The key points of restoration are:

  • Locate the encrypted entrance through browser breakpoints;
  • Complete the browser environment required by Node.js;
  • Cut out and rewritemnsv2Core functions;
  • Correctly handle parameter splicing order and custom Base64 encoding.

Once the above steps are completed, legal signatures can be stably generated locally, and automated data collection can be realized using languages ​​such as Python. I hope this note can provide you with ideas in similar reverse analysis.