Anthony Smith
Tony's Tech Blog

Tony's Tech Blog

Obfuscate JavaScript code using Node.js

Obfuscate JavaScript code using Node.js

A series of code transformations that turn plain, easy-to-read JS code into a modified version that is extremely hard to understand and reverse-engine

Anthony Smith's photo
Anthony Smith
·Jul 18, 2022·

7 min read

Subscribe to my newsletter and never miss my upcoming articles

Table of contents

Use the JavaScript Obfuscator library in Node.js to obfuscate JavaScript code

As a developer, you spend a lot of time building, developing, and debugging your code. The last thing you want is for someone else to have the ability to steal all of your hard work and reuse it somewhere else. Thankfully, there is a common practice called code obfuscation that can help protect your work from the eyes of the public.

Obfuscation is a technique that deliberately obfuscates the source code of a JavaScript file which makes it difficult for humans to understand. It is similar to encryption, but a computer program can still understand the code and can execute it.

Analyzing obfuscated code

function _0x5e31() {
    const _0x397c79 = ['330dUcfrS', '42xzDbkb', 'axios', '300632rOPemi', 'express', '131085njtadB', 'then', 'cheerio', 'log', '4967757RghcFl', 'find', 'each', '.fc-sublink__title', '123225NWjXfk', '5124564IcBrVv', 'catch', '12422pNvrdc', 'https://www.theguardian.com/uk', 'listen', 'load', 'text', 'href', 'push', '18546374VDQISy', '188BnKquS', '241KqzJHu', 'server\x20running\x20on\x20PORT\x20'];
    _0x5e31 = function () {
        return _0x397c79;
    };
    return _0x5e31();
}
const _0x54f094 = _0x1fe5;

function _0x1fe5(_0x595903, _0x512277) {
    const _0x5e312a = _0x5e31();
    return _0x1fe5 = function (_0x1fe571, _0x2f73c5) {
        _0x1fe571 = _0x1fe571 - 0xa2;
        let _0x314c44 = _0x5e312a[_0x1fe571];
        return _0x314c44;
    }, _0x1fe5(_0x595903, _0x512277);
}(function (_0x9851f3, _0x17fda5) {
    const _0x49419b = _0x1fe5,
        _0x3f4b41 = _0x9851f3();
    while (!![]) {
        try {
            const _0x4ec293 = -parseInt(_0x49419b(0xa9)) / 0x1 * (parseInt(_0x49419b(0xbb)) / 0x2) + parseInt(_0x49419b(0xb4)) / 0x3 + -parseInt(_0x49419b(0xa8)) / 0x4 * (parseInt(_0x49419b(0xb8)) / 0x5) + parseInt(_0x49419b(0xb9)) / 0x6 + -parseInt(_0x49419b(0xac)) / 0x7 * (parseInt(_0x49419b(0xae)) / 0x8) + parseInt(_0x49419b(0xb0)) / 0x9 * (-parseInt(_0x49419b(0xab)) / 0xa) + parseInt(_0x49419b(0xa7)) / 0xb;
            if (_0x4ec293 === _0x17fda5) break;
            else _0x3f4b41['push'](_0x3f4b41['shift']());
        } catch (_0x7c6393) {
            _0x3f4b41['push'](_0x3f4b41['shift']());
        }
    }
}(_0x5e31, 0xcbcca));
const PORT = 0x1f40,
    axios = require(_0x54f094(0xad)),
    cheerio = require(_0x54f094(0xb2)),
    express = require(_0x54f094(0xaf)),
    app = express(),
    url = _0x54f094(0xbc);
axios(url)[_0x54f094(0xb1)](_0x503c28 => {
    const _0xe17be9 = _0x54f094,
        _0x2c6965 = _0x503c28['data'],
        _0x3db981 = cheerio[_0xe17be9(0xa3)](_0x2c6965),
        _0x3da326 = [];
    _0x3db981(_0xe17be9(0xb7), _0x2c6965)[_0xe17be9(0xb6)](function () {
        const _0x4fc109 = _0xe17be9,
            _0x559919 = _0x3db981(this)[_0x4fc109(0xa4)](),
            _0x474597 = _0x3db981(this)[_0x4fc109(0xb5)]('a')['attr'](_0x4fc109(0xa5));
        _0x3da326[_0x4fc109(0xa6)]({
            'title': _0x559919,
            'url': _0x474597
        });
    }), console[_0xe17be9(0xb3)](_0x3da326);
})[_0x54f094(0xba)](_0xae011d => console[_0x54f094(0xb3)](_0xae011d)), app[_0x54f094(0xa2)](PORT, () => console['log'](_0x54f094(0xaa) + PORT));

As you can tell, this code has been successfully obfuscated. It's pretty hard to understand, isn't it? You'll have to read it carefully to understand what is happening which could take you days to do.

Here is the original unobfuscated code:

// This example uses axios, cheerio & Express to create a server and scrape some data
const PORT = 8000;
const axios = require('axios');
const cheerio = require('cheerio');
const express = require('express');
const app = express();
const url = 'https://www.theguardian.com/uk';

axios(url)
    .then(response => {
        const html = response.data
        const $ = cheerio.load(html)
        const articles = [];
        $('.fc-sublink__title', html).each(function() {
            const title = $(this).text();
            const url = $(this).find('a').attr('href');
            articles.push({
                title,
                url
            })
        })
        console.log(articles)
    }).catch(err => console.log(err))
app.listen(PORT, () => console.log(`server running on PORT ${PORT}`))

That looks pretty different, doesn't it? This code is effectively creating an express server and scraping some data from a provided URL, it can be easily stolen and re-used somewhere else without your permission if a hacker were to find it in your code. If this was sensitive company data that you don't want the world to see, obfuscating is the way to go. This is also useful for people who sell code on platforms like ThemeForest, Template Monster, and others where your code could easily be copied from the browser and pasted into a text editor; however, with an obfuscator, it can still be copied, but it would be extremely hard to read and decrypt.

In this article, I'll be showing you how you can obfuscate your code with the JavaScript Obfuscator library in Node.js.

When and why should you obfuscate your JavaScript code?

One of the most frustrating things about JavaScript is that it is easy to understand by most developers, meaning it can be stolen and reused elsewhere without the developer's permission. The last thing you want is for someone else to capture and reuse all the code YOU wrote.

I think it makes sense to obfuscate, to make it harder, if not impossible, for someone to disassemble. Also, I think it makes sense for products involving a large customer base where you do not want your competitors to know much about your products.

How to obfuscate your JavaScript code?

We're going to be using Node.js and the JavaScript Obfuscator module to obfuscate our JavaScript code. The javaScript obfuscator module is a free and open source tool with a wide number of features that can protect your code from being stolen.

Key Features:

  • Control flow flattening
  • Various code transformations
  • CLI options
  • Has no restrictions or limits
  • Can run on your local machine or on a remote server as it does not require any internet connection
  • Variables renaming
  • Dead code injection

Installation:

npm install javascript-obfuscator

After installing the module, you can require it in your code using require("javascript-obfuscator").

Using the module:

Now we can begin using the obfuscator module. We must first create an instance of the obfuscator module; from there, we can use the obfuscate method to obfuscate our code. It expects a first argument, which is the code to obfuscate, and a second argument which is an object containing the options to use.

// Require the JavaScript obfuscator
var JavaScriptObfuscator = require('javascript-obfuscator');

// Obfuscate the code providen as first argument
var obfuscationResult = JavaScriptObfuscator.obfuscate(`
function myfunction() {
  console.log("function");
};

myfunction();

function calculate(x, y, op) {
  var answer;
  if ( op = "add" ) {
    answer = x + y;
  };
  else if ( op = "sub" ) {
    answer = x - y;
  };
  else if ( op = "multi" ) {
    answer = x * y;
  };
  else if ( op = "divide" ) {
    answer = x / y;
  };
  else {
    answer = "Error";
  };
  return answer;
};
`);
console.log(obfuscationResult.getObfuscatedCode());

Options:

You can customize the obfuscator module by passing an object containing the options to use.

JavaScriptObfuscator.obfuscate(YourCode, {
    compact: true,
    controlFlowFlattening: false,
    controlFlowFlatteningThreshold: 0.75,
    deadCodeInjection: false,
    deadCodeInjectionThreshold: 0.4,
    debugProtection: false,
    debugProtectionInterval: false,
    disableConsoleOutput: false,
    domainLock: [],
    log: false,
    mangle: false,
    renameGlobals: false,
    reservedNames: [],
    rotateStringArray: true,
    seed: 0,
    selfDefending: false,
    sourceMap: false,
    sourceMapBaseUrl: '',
    sourceMapFileName: '',
    sourceMapMode: 'separate',
    stringArray: true,
    stringArrayEncoding: false,
    stringArrayThreshold: 0.75,
    target: 'browser',
    unicodeEscapeSequence: false
});

You should check out the official documentation of the library to see all the new options that are presently available. You can also check out the JavaScript Obfuscator Github to see all the options that are currently available. Currently, they provide presets which include a feeling of "low", "medium", "high", and "highest".

A. Low Obfuscation

{
    compact: true,
    controlFlowFlattening: false,
    deadCodeInjection: false,
    debugProtection: false,
    debugProtectionInterval: false,
    disableConsoleOutput: true,
    log: false,
    mangle: true,
    renameGlobals: false,
    rotateStringArray: true,
    selfDefending: true,
    stringArray: true,
    stringArrayEncoding: false,
    stringArrayThreshold: 0.75,
    unicodeEscapeSequence: false
}

B. Medium Obfuscation

{
    compact: true,
    controlFlowFlattening: true,
    controlFlowFlatteningThreshold: 0.75,
    deadCodeInjection: true,
    deadCodeInjectionThreshold: 0.4,
    debugProtection: false,
    debugProtectionInterval: false,
    disableConsoleOutput: true,
    log: false,
    mangle: false,
    renameGlobals: false,
    rotateStringArray: true,
    selfDefending: true,
    stringArray: true,
    stringArrayEncoding: 'base64',
    stringArrayThreshold: 0.75,
    unicodeEscapeSequence: false
}

C. High Obfuscation

{
    compact: true,
    controlFlowFlattening: true,
    controlFlowFlatteningThreshold: 1,
    deadCodeInjection: true,
    deadCodeInjectionThreshold: 1,
    debugProtection: true,
    debugProtectionInterval: true,
    disableConsoleOutput: true,
    log: false,
    mangle: false,
    renameGlobals: false,
    rotateStringArray: true,
    selfDefending: true,
    stringArray: true,
    stringArrayEncoding: 'rc4',
    stringArrayThreshold: 1,
    unicodeEscapeSequence: false
}

Try it out!

We're going to go ahead and read the content of a JS file and then obfuscate it.

// Require Filesystem module
var fs = require("fs");

// Require the Obfuscator Module
var JavaScriptObfuscator = require('javascript-obfuscator');

// Read the file of your original JavaScript Code as text
fs.readFile('./your-original-code.js', "UTF-8", function(err, data) {
    if (err) {
        throw err;
    }

    // Obfuscate content of the JS file
    var obfuscationResult = JavaScriptObfuscator.obfuscate(data);

    // Write the obfuscated code into a new file
    fs.writeFile('./your-code-obfuscated.js', obfuscationResult.getObfuscatedCode() , function(err) {
        if(err) {
            return console.log(err);
        }
        console.log("The file was saved!");
    });
});

As you can see, the above code is now obfuscated, making it much harder for humans to understand. This is a great way to protect your JavaScript code from hackers and prying eyes.

Obfuscation is a great way to hide business logic from the outside world, and it will also make the size of your js file much smaller so it can be more easily transferred.


Thank you so much for reading!

If you enjoy reading my content, follow me on Twitter to learn more about my journey in tech!

Did you find this article valuable?

Support Anthony Smith by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
 
Share this