JavaScript reverse engineering and protection technology analysis

1. Introduction

When you are browsing for hidden prices, grabbing reservation codes, and using web-based widgets every day, have you ever been curious - why is it useless to copy the API request package captured by others? Why can't I see the complete and readable code when I open the Sources panel of the browser? Behind this are two lines of defense: the "black boxing" of front-end interaction logic and the "access lock" of back-end API calls. This article will quickly explain these down-to-earth technical routines from two aspects: Front-end JS protection actual combat and Simple reverse disassembly ideas.


2. Lightweight data protection solution for website front-end + back-end

Let’s first look at a complete “anti-malicious call” combo: the backend locks API parameters, and the frontend hides the logic for generating parameters.

2.1 URL/API parameter encryption/verification

Core function: Prevent unauthorized crawlers or individuals from directly copying API request package calls Common implementation methods:

  1. Front-end and back-end** jointly maintain algorithm rules/keys**
  2. Commonly used tool chains: Base64/Hex (simple escape), MD5/SHA256 (irreversible tamper-proof), AES/DES (reversible encryption of sensitive content), RSA (for key exchange)
  3. Typical product scenarios:
  • Ticket grabbing platform: Each request will bring updatessign(sign),ts(timestamp),nonce(random number)
  • E-commerce hidden price: the encrypted data returned by the API is decrypted before display

2.2 JavaScript front-end code protection technology

The front end is the place closest to the user. The logic of generating parameters and the steps of decrypting data will be exposed here, so "black box reinforcement" is needed. There are three commonly used ones: code compression, code obfuscation, and WebAssembly.

2.2.1 Code compression

Entry-level protection: The main purpose is not to protect against people, but to optimize the loading speed + blur the variable short names by the way, and it can be read after it is fully formatted. Principle: Remove all redundancies (spaces, newlines, comments), and change long variable/function names into abbreviations of 1-2 characters Commonly used tools: Terser (the default compression tool for Webpack/Vite/Rollup) Example comparison:

// 🟢 压缩前(人人都能读)
function calculateDiscountPrice(originalPrice, discountPercent, isVip) {
  // VIP额外打9折
  if (isVip) {
    discountPercent += 10;
  }
  // 计算最终价格,保留两位小数
  const finalPrice = originalPrice * (1 - discountPercent / 100);
  return Number(finalPrice.toFixed(2));
}

// 🔴 压缩后(稍微有点晃眼,但格式化后秒懂)
function calculateDiscountPrice(o,d,i){return i&&(d+=10),Number((o*(1-d/100)).toFixed(2))}

2.2.2 Code obfuscation

Advanced level protection: Specially used for "disgusting contrarians". Even if it is formatted, it is like reading a bible. The difficulty of restoration depends on the level of obfuscation. Comparison of core obfuscation methods:

Obfuscation technologySpecific operationsActual effects (impact on contrarians)
Variable/function name obfuscationPut meaningfulgetTokenReplace with_0x1234orhex16StringThe semantic association of variables is completely lost, and the key logical entry cannot be found
String confusion"Hello" "https://api.example.com」等明文存到数组里,用下标取,甚至再加Base64/Unicode转码Cannot use the simple method of "global search API domain name" to locate the request
Control flow flatteningif/else/forThis normal process is broken down intoswitch-case + 随机状态值out-of-order structureBreakpoint debugging does not know where to jump next
Debug protectionInsert infinite loopdebugger, and even detects that the page freezes when the developer tool is openedYou cannot use the breakpoint tool that comes with the browser to trace the code
Domain name lockBind the specified domain name when obfuscation, and directly report an error when placed on other websites/local debuggingYou cannot copy the obfuscation code to your own environment for testing

Most commonly used free obfuscation tools:javascript-obfuscator(There is an online version and a CLI/Node.js version)


2.2.3 WebAssembly

Hard-core protection: Directly move the core logic of the front-end (such as the algorithm for generating complex signs and the steps to decrypt sensitive data) from JS to C/C++/Rust, and compile it into a binary file.wasm. JS is only responsible for calling. **Why is it difficult to reverse? **

  • It is not a readable JS code, but a binary that the machine can run directly.
  • Reverse requires knowledge of assembly language and the threshold is high
  • It is faster and smaller than JS, and developers are willing to use it

3. JavaScript obfuscation in practice (usejavascript-obfuscator

The Node.js version is used here for demonstration, which is suitable for batch obfuscation of project code.

3.1 Basic obfuscation configuration

First add the basic "code compression + variable modification + string hiding":

// 引入依赖,先 npm install javascript-obfuscator
const JavaScriptObfuscator = require('javascript-obfuscator');
const fs = require('fs');

// 读取要混淆的原始代码
const originalCode = fs.readFileSync('original.js', 'utf8');

// 基础但有效的混淆选项
const obfuscationOptions = {
  compact: true, // 压缩成一行(可选false)
  controlFlowFlattening: true, // 必加!核心的控制流打乱
  stringArray: true, // 必加!字符串阵列化
  stringArrayEncoding: ['base64'], // 字符串再加一层Base64
  selfDefending: true, // 必加!自我保护,格式化后代码也不能正常运行
};

// 生成混淆后的代码
const obfuscatedResult = JavaScriptObfuscator.obfuscate(originalCode, obfuscationOptions);
const obfuscatedCode = obfuscatedResult.getObfuscatedCode();

// 写入文件
fs.writeFileSync('obfuscated.js', obfuscatedCode);
console.log('混淆完成!已生成 obfuscated.js');

3.2 Advanced obfuscation configuration (optional)

If you have sufficient budget/high security requirements, you can add these options (note: the higher the obfuscation level, the slower the code will run):

const advancedOptions = {
  // 变量名改成更复杂的hex16字符串,甚至混淆全局变量
  identifierNamesGenerator: 'hexadecimal',
  renameGlobals: true,

  // 无限循环的debugger,打开开发者工具就不停弹
  debugProtection: true,
  debugProtectionInterval: true,

  // 绑定指定域名,防止复制到其他地方用
  domainLock: ['your-website.com', 'www.your-website.com'],

  // 字符串再加一层Unicode转码,看起来更乱
  unicodeEscapeSequence: true,
};

4. WebAssembly simple example (using Emscripten)

Suppose we want to move the "core function for calculating dynamic sign" to WASM:

4.1 Writing C code

// 保存为 add_sign.c(模拟简单的加法+拼接生成sign)
#include <string.h>
#include <stdio.h>

// 拼接数字和固定字符串生成sign(真实场景可以更复杂)
void generate_sign(int ts, int nonce, char* output, int output_len) {
  snprintf(output, output_len, "sign_%d_%d", ts, nonce);
}

4.2 Compile to WASM with Emscripten

First install Emscripten (the official website has a one-click installation script), and then run:

emcc add_sign.c -o add_sign.js -s WASM=1 -s EXPORTED_FUNCTIONS="['_generate_sign']" -s EXPORTED_RUNTIME_METHODS="['ccall', 'cwrap']"

Two files will be generated:add_sign.js(JS calls WASM bridge),add_sign.wasm(core binary logic)


4.3 Call in web page

<!DOCTYPE html>
<html>
<head>
  <title>WASM生成Sign示例</title>
</head>
<body>
  <script src="add_sign.js"></script>
  <script>
    // 等WASM加载完成
    Module.onRuntimeInitialized = function() {
      // 把C函数转成JS能直接调用的函数
      const generateSign = Module.cwrap('generate_sign', null, ['number', 'number', 'string', 'number']);

      // 生成动态参数
      const ts = Date.now();
      const nonce = Math.floor(Math.random() * 1000000);

      // 分配内存给输出的sign
      const outputBuffer = Module._malloc(100);
      generateSign(ts, nonce, outputBuffer, 100);

      // 读取内存里的sign
      const sign = Module.UTF8ToString(outputBuffer);
      console.log('生成的动态参数:', { ts, nonce, sign });

      // 释放内存
      Module._free(outputBuffer);
    };
  </script>
</body>
</html>

5. Simple ideas for reverse analysis (for security self-examination)

If the website is already online, you can use these simple methods to check whether the protection is effective:

5.1 Browser Developer Tools Self-Check

  1. Network Panel: Search globally for "api", "get", "sign" and "token" to see if the API request has encrypted parameters and whether sensitive data is returned in plain text.
  2. Sources Panel: Find a JS file and click "Format Code" (the one in the lower left corner{}button) to see if you can find the key logic
  3. Console panel: PressF12Open it and see if it keeps playing.debugger, or report an error directly

5.2 Hook simple API self-examination

If you still cannot find the logic for generating parameters after formatting, you can use Hook key function to capture the parameters:

// Hook Fetch请求(复制到Console面板运行)
const originalFetch = window.fetch;
window.fetch = function(url, options) {
  console.log('🔍 拦截到Fetch请求:');
  console.log('URL:', url);
  console.log('Options:', options);
  return originalFetch.apply(this, arguments);
};

// Hook XMLHttpRequest请求(复制到Console面板运行)
const originalXHR = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url) {
  console.log('🔍 拦截到XHR请求:');
  console.log('Method:', method);
  console.log('URL:', url);
  this._method = method;
  this._url = url;
  return originalXHR.apply(this, arguments);
};

6. Practical protection suggestions (performance and security balance)

Don't pursue "100% irreversible" (there is no absolutely safe code). The key point is to increase the time cost of reverse engineering and make malicious callers give up:

  1. Layered protection, special processing of core logic:
  • Ordinary page interaction logic: only use code compression
  • Logic for generating simple parameters: obfuscation with basics
  • Logic for generating core sign and decrypting sensitive data: using WebAssembly
  1. Update the obfuscation strategy/key regularly: For example, change it once a weekjavascript-obfuscatorRandom seed or configuration of
  2. Control obfuscation level: Advanced obfuscation will slow down the code running speed by 10%-30%. Do not add too advanced obfuscation to core interactions (such as button clicks and drop-down loading)
  3. Don’t just rely on front-end protection: The back-end must add authentication (token, IP current limit, sign verification), the front-end is just the first buffer.

7. Summary

The protection of modern web applications is a combination of "front-end + back-end":

  • Backend: Lock API parameters (sign, ts, nonce), IP current limit, and authentication
  • Front-end: compress common logic, confuse important logic, and move core logic to WebAssembly

Understanding these technologies can not only help developers build more secure websites, but also help security researchers quickly locate vulnerabilities.


Sample code repository: https://github.com/Python3WebSpider/JavaScriptObfuscate