難読化ライブラリ「JavaScript Obfuscator」オプション一覧

はじめに

JavaScriptの難読化で調べると最有力として挙がる「JavaScript Obfuscator」。オプションの項目を眺めていると、難読化以外にも色々機能が実装されていて面白かったので、README.mdのオプション項目の日本語訳という形で紹介します。シンプルな英語だし訳いらんというのは(ry

JavaScript Obfuscator オプション

JS Obfuscatorでは次のオプションを使用できます。

options:

{
    compact: true,
    controlFlowFlattening: false,
    controlFlowFlatteningThreshold: 0.75,
    deadCodeInjection: false,
    deadCodeInjectionThreshold: 0.4,
    debugProtection: false,
    debugProtectionInterval: false,
    disableConsoleOutput: false,
    domainLock: [],
    identifierNamesGenerator: 'hexadecimal',
    identifiersPrefix: '',
    inputFileName: '',
    log: false,
    renameGlobals: false,
    reservedNames: [],
    reservedStrings: [],
    rotateStringArray: true,
    seed: 0,
    selfDefending: false,
    sourceMap: false,
    sourceMapBaseUrl: '',
    sourceMapFileName: '',
    sourceMapMode: 'separate',
    stringArray: true,
    stringArrayEncoding: false,
    stringArrayThreshold: 0.75,
    target: 'browser',
    transformObjectKeys: false,
    unicodeEscapeSequence: false
}

CLI options:

    -v, --version
    -h, --help

    -o, --output

    --compact <boolean>
    --config <string>
    --control-flow-flattening <boolean>
    --control-flow-flattening-threshold <number>
    --dead-code-injection <boolean>
    --dead-code-injection-threshold <number>
    --debug-protection <boolean>
    --debug-protection-interval <boolean>
    --disable-console-output <boolean>
    --domain-lock '<list>' (comma separated)
    --exclude '<list>' (comma separated)
    --identifier-names-generator <string> [hexadecimal, mangled]
    --identifiers-prefix <string>
    --log <boolean>
    --rename-globals <boolean>
    --reserved-names '<list>' (comma separated)
    --reserved-strings '<list>' (comma separated)
    --rotate-string-array <boolean>
    --seed <number>
    --self-defending <boolean>
    --source-map <boolean>
    --source-map-base-url <string>
    --source-map-file-name <string>
    --source-map-mode <string> [inline, separate]
    --string-array <boolean>
    --string-array-encoding <boolean|string> [true, false, base64, rc4]
    --string-array-threshold <number>
    --target <string> [browser, browser-no-eval, node]
    --transform-object-keys <boolean>
    --unicode-escape-sequence <boolean>

compact

型: boolean デフォルト値: true

コードを1行に纏める。

config

型: string デフォルト値: ``

オプション設定を別ファイル化した場合のファイル名。(JS,JSONファイルに対応)これらは、CLIに直接渡されるオプションによってオーバーライドされます。

controlFlowFlattening

型: boolean デフォルト値: false

:warning: このオプションは実行速度への影響が大きく、最大1.5倍遅くなります。 controlFlowFlatteningThreshold の項目で影響を受けるノードの割合を設定できます。

コード制御フローのフラット化を有効にします。ソースコードの構造を変換することにより、プログラムの流れを理解し辛くします。

例:

// input
(function(){
    function foo () {
        return function () {
            var sum = 1 + 2;
            console.log(1);
            console.log(2);
            console.log(3);
            console.log(4);
            console.log(5);
            console.log(6);
        }
    }

    foo()();
})();

// output
(function () {
    function _0x3bfc5c() {
        return function () {
            var _0x3260a5 = {
                'WtABe': '4|0|6|5|3|2|1',
                'GokKo': function _0xf87260(_0x427a8e, _0x43354c) {
                    return _0x427a8e + _0x43354c;
                }
            };
            var _0x1ad4d6 = _0x3260a5['WtABe']['split']('|'), _0x1a7b12 = 0x0;
            while (!![]) {
                switch (_0x1ad4d6[_0x1a7b12++]) {
                case '0':
                    console['log'](0x1);
                    continue;
                case '1':
                    console['log'](0x6);
                    continue;
                case '2':
                    console['log'](0x5);
                    continue;
                case '3':
                    console['log'](0x4);
                    continue;
                case '4':
                    var _0x1f2f2f = _0x3260a5['GokKo'](0x1, 0x2);
                    continue;
                case '5':
                    console['log'](0x3);
                    continue;
                case '6':
                    console['log'](0x2);
                    continue;
                }
                break;
            }
        };
    }

    _0x3bfc5c()();
}());

controlFlowFlatteningThreshold

型: number デフォルト値: 0.75 最小値: 0 最大値: 1

controlFlowFlattening による変換がノードに適用される確率。
大量の制御フロー変換によりコードの速度が低下し、コードサイズが増加する可能性があるため、元のコードサイズが大きい場合にこの設定は特に役立ちます。
controlFlowFlatteningThreshold:0controlFlowFlattening:falseと同じです。

deadCodeInjection

型: boolean デフォルト値: false

:warning: コードのサイズが劇的に増加します(最大200%)。コードのサイズより難読化の方が重要な場合のみ使用します。 deadCodeInjectionThreshold の項目で影響を受けるノードの割合を設定できます。
:warning: このオプションは、 stringArrayオプションを強制的に有効にします。

到達不能なランダムブロックが追加されます。

例:

// input
(function(){
    if (true) {
        var foo = function () {
            console.log('abc');
            console.log('cde');
            console.log('efg');
            console.log('hij');
        };

        var bar = function () {
            console.log('klm');
            console.log('nop');
            console.log('qrs');
        };

        var baz = function () {
            console.log('tuv');
            console.log('wxy');
            console.log('z');
        };

        foo();
        bar();
        baz();
    }
})();

// output
var _0x5024 = [
    'zaU',
    'log',
    'tuv',
    'wxy',
    'abc',
    'cde',
    'efg',
    'hij',
    'QhG',
    'TeI',
    'klm',
    'nop',
    'qrs',
    'bZd',
    'HMx'
];
var _0x4502 = function (_0x1254b1, _0x583689) {
    _0x1254b1 = _0x1254b1 - 0x0;
    var _0x529b49 = _0x5024[_0x1254b1];
    return _0x529b49;
};
(function () {
    if (!![]) {
        var _0x16c18d = function () {
            if (_0x4502('0x0') !== _0x4502('0x0')) {
                console[_0x4502('0x1')](_0x4502('0x2'));
                console[_0x4502('0x1')](_0x4502('0x3'));
                console[_0x4502('0x1')]('z');
            } else {
                console[_0x4502('0x1')](_0x4502('0x4'));
                console[_0x4502('0x1')](_0x4502('0x5'));
                console[_0x4502('0x1')](_0x4502('0x6'));
                console[_0x4502('0x1')](_0x4502('0x7'));
            }
        };
        var _0x1f7292 = function () {
            if (_0x4502('0x8') === _0x4502('0x9')) {
                console[_0x4502('0x1')](_0x4502('0xa'));
                console[_0x4502('0x1')](_0x4502('0xb'));
                console[_0x4502('0x1')](_0x4502('0xc'));
            } else {
                console[_0x4502('0x1')](_0x4502('0xa'));
                console[_0x4502('0x1')](_0x4502('0xb'));
                console[_0x4502('0x1')](_0x4502('0xc'));
            }
        };
        var _0x33b212 = function () {
            if (_0x4502('0xd') !== _0x4502('0xe')) {
                console[_0x4502('0x1')](_0x4502('0x2'));
                console[_0x4502('0x1')](_0x4502('0x3'));
                console[_0x4502('0x1')]('z');
            } else {
                console[_0x4502('0x1')](_0x4502('0x4'));
                console[_0x4502('0x1')](_0x4502('0x5'));
                console[_0x4502('0x1')](_0x4502('0x6'));
                console[_0x4502('0x1')](_0x4502('0x7'));
            }
        };
        _0x16c18d();
        _0x1f7292();
        _0x33b212();
    }
}());

deadCodeInjectionThreshold

型: number デフォルト値: 0.4 最小値: 0 最大値: 1

deadCodeInjectionの影響を受けるノードの割合を設定できます。

debugProtection

型: boolean デフォルト値: false

:warning: ブラウザの開発者ツールを開いた場合、ブラウザがフリーズします。

このオプションにより、WebKitベースのブラウザとMozilla Firefoxの両方で開発者ツールのコンソールタブを使用することはほとんど不可能になります。

  • WebKitベース:サイトウィンドウをブロックしますが、デベロッパーツールパネルはナビゲートできます。
  • Firefox:サイトウィンドウをブロックしませんが、それでも開発者ツールを使用できません。

debugProtectionInterval

型: boolean デフォルト値: false

:warning: ブラウザがフリーズします! 自己責任で使用してください。

開発者ツールを開いていると、定期的に[コンソール]タブのデバッグモードに切り替わるようになり、他の機能の使用が難しくなります。 debugProtectionが有効になっている場合に機能します。

disableConsoleOutput

型: boolean デフォルト値: false

console.log, console.info, console.error, console.warn, console.debug, console.exception, console.traceを空の関数に置き換えて使用できなくします。これによりデバッガーの使用が難しくなります。

domainLock

型: string[] デフォルト値: []

特定のドメインおよび/またはサブドメインでのみ実行されるようにロックします。これは、ソースコードをコピーして貼り付け、他の場所で実行するだけの人にとっては非常に困難です。

複数のドメインとサブドメイン

複数のドメインまたはサブドメインにロックすることも可能です。 たとえば、コードが www.example.com でのみ実行されるようにするにはwww.example.comを追加し、example.comのサブドメインで機能させるには、.example.comを使用します。

exclude

型: string[] デフォルト値: []

ファイル名またはグロブを設定すると難読化から除外できます。

identifierNamesGenerator

型: string デフォルト値: hexadecimal

識別子名ジェネレータを設定します。

利用可能な値:
* hexadecimal: 識別子名が_0xabc123のようになります。
* mangled: 識別子名がa, b, cのようになります。

identifiersPrefix

型: string デフォルト値: ''

すべてのグローバル識別子のプレフィックスを設定します。
複数のファイルを難読化する場合は、このオプションを使用します。 このオプションは、これらのファイルのグローバル識別子間の競合を回避するのに役立ちます。 プレフィックスはファイルごとに異なる必要があります。

inputFileName

型: string デフォルト値: ''

入力ファイルの名前をソースコードで設定できます。 この名前は、ソースマップの生成に内部的に使用されます。

log

型: boolean デフォルト値: false

開発者ツールのコンソールへのロギングを有効にします。

renameGlobals

型: boolean デフォルト値: false

:warning: このオプションはコードを破壊する可能性があります。それが何をするか知っている場合にのみ有効にしてください!

グローバル変数および関数名の難読化を宣言付きで有効にします。

reservedNames

型: string[] デフォルト値: []

識別子名が設定した正規表現と一致した場合、難読化と識別子の生成が無効になります。

例:

    {
        reservedNames: [
            '^someVariable',
            'functionParameter_\d'
        ]
    }

reservedStrings

型: string[] デフォルト値: []

文字列が設定した正規表現と一致した場合、変換が無効になります。

例:

    {
        reservedStrings: [
            'react-native',
            '\.\/src\/test',
            'some-string_\d'
        ]
    }

rotateStringArray

型: boolean デフォルト値: true

:warning: stringArrayが有効である必要があります

固定されたランダムな(コードの難読化で生成された)場所で stringArray配列をシフトします。 これにより、削除された文字列の順序を元の場所に一致させることが難しくなります。

元のソースコードが小さくない場合は、このオプションをお勧めします。ヘルパー関数が注目を集めることができるためです。

seed

型: number デフォルト値: 0

乱数生成器のシードを設定します。これは、繰り返し可能な結果を作成するのに役立ちます。シードが0の場合、乱数生成器はシードなしで機能します。

selfDefending

型: boolean デフォルト値: false

:warning: このオプションで難読化した後は、難読化されたコードを変更しないでください。uglifyingは自己防衛を引き起こし、コードは機能しなくなります!
:warning: このオプションは強制的に compacttrueに設定します

コード整形(フォーマット)および変数名変更に対して耐性のあるコードを出力します。出力されたコードに対してこれらを行うと機能しなくなるため、解析と変更が難しくなります。

sourceMap

型: boolean デフォルト値: false

難読化されたコードのソースマップ生成を有効にします。
ソースマップは、難読化されたJavaScriptソースコードのデバッグに役立ちます。 本番環境でデバッグする場合、またはデバッグする必要がある場合は、別のソースマップファイルを秘密の場所にアップロードしてから、ブラウザをそこを参照することができます。

sourceMapBaseUrl

型: string デフォルト値: ``

ソースマップのインポートURLの値を設定します。sourceMapMode: 'separate'の場合のみ有効です。

CLI 例:

javascript-obfuscator input.js --output out.js --source-map true --source-map-base-url 'http://localhost:9000'

結果:

//# sourceMappingURL=http://localhost:9000/out.js.map

sourceMapFileName

型: string デフォルト値: ``

出力されるソースマップのファイル名を設定します。sourceMapMode: 'separate'の場合のみ有効です。

CLI 例:

javascript-obfuscator input.js --output out.js --source-map true --source-map-base-url 'http://localhost:9000' --source-map-file-name example

結果:

//# sourceMappingURL=http://localhost:9000/example.js.map

sourceMapMode

型: string デフォルト値: separate

ソースマップの生成モードを指定します。:
* inline - 個別のファイルを持つ代わりに、ソースマップを含む単一のファイルを出力します。;
* separate - 対応する '.map'ファイルをソースマップとともに生成します。CLIで実行する場合、難読化されたコード //#sourceMappingUrl = file.js.mapを使用して、ソースマップファイルへのリンクをファイルの最後に追加します。

stringArray

型: boolean デフォルト値: true

文字列リテラルを削除し特別な配列に置き換えます。たとえばvar m = "Hello World"というようなコードでは"Hello World"の部分が置き換えられ、var m = _0x12c456 [0x1];のようになります。

stringArrayEncoding

型: boolean|string デフォルト値: false

:warning: stringArrayオプションが有効になっている必要があります。

このオプションは、スクリプトの速度を低下させる可能性があります。
base64またはrc4を使用してstringArrayのすべての文字列リテラルをエンコードし、実行時にデコードするために使用される特別なコードを挿入します。

利用可能な値:
* true (boolean): base64stringArrayをエンコードします。
* false (boolean): stringArrayをエンコードしません。
* 'base64' (string): base64stringArrayをエンコードします。
* 'rc4' (string): rc4stringArrayをエンコードします。 base64と比べ約30-50%遅くなりますが, 初期値を取得するのがより難しくなります。 この設定を使用する際はサイズが非常に大きくなるのを防ぐため、 unicodeEscapeSequenceは無効にすることをお勧めします。

stringArrayThreshold

型: number デフォルト値: 0.8 最小値: 0 最大値: 1

:warning: stringArrayを有効にする必要があります。

文字列リテラルが stringArrayに挿入される確率(0〜1)を調整できます。
string arrayを繰り返し呼び出し、コードの速度を低下させる可能性があるため、コードサイズが大きい場合にこの設定は特に役立ちます。
stringArrayThreshold: 0stringArray: falseと同じ結果になります.

target

型: string デフォルト値: browser

難読化されたコードのターゲット環境を設定できます。

利用可能な値:
* browser;
* browser-no-eval;
* node.

現在、browserおよびnodeターゲットの出力コードは同じです。
browser-no-evalターゲットの出力コードはevalを使用していません。

transformObjectKeys

型: boolean デフォルト値: false

オブジェクトのキーの変換を有効にします。

例:

// input
(function(){
    var object = {
        foo: 'test1',
        bar: {
            baz: 'test2'
        }
    };
})();

// output
var _0x5a21 = [
    'foo',
    'test1',
    'bar',
    'baz',
    'test2'
];
var _0x223f = function (_0x474dc0, _0x10db96) {
    _0x474dc0 = _0x474dc0 - 0x0;
    var _0x4c8bf7 = _0x5a21[_0x474dc0];
    return _0x4c8bf7;
};
(function () {
    var _0x2e1a8e = {};
    _0x2e1a8e[_0x223f('0x0')] = _0x223f('0x1');
    _0x2e1a8e[_0x223f('0x2')] = {};
    _0x2e1a8e[_0x223f('0x2')][_0x223f('0x3')] = _0x223f('0x4');
}());

unicodeEscapeSequence

型: boolean デフォルト値: false

Unicodeエスケープシーケンスへの文字列変換を有効/無効にすることができます。

Unicodeエスケープシーケンスによりコードサイズが大幅に増加し、文字列を簡単に元のビューに戻すことができます。 このオプションは、小さなソースコードに対してのみ有効にすることをお勧めします。

推奨オプション設定

高難読化, 低パフォーマンス

50%~100%パフォーマンスが遅くなります。

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

中難読化, 最適性能

30%~35%パフォーマンスが遅くなります。

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

低難読化、高パフォーマンス

パフォーマンスはわずかに遅くなります。

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

感想

ただの高性能難読化ツールだと思っていたので、ドメインロックや開発者ツールキラーまで実装されてて驚きました。あと自己防衛機能(selfDefending)は発想すらなくて感心しました。どんな実装してんだろ……(なお調べる気力はない)
プラグインとしてWebpack,Gulp,Grunt等色々対応してますし、オンライン版まであるので、完成間近/完成したプロダクトをお持ちの方は試してみてはいかがでしょうか。
ちなみに現在はES2019まで対応しています。当然のことながら最新構文の対応は若干遅れますので、ハマらないよう注意しましょう。特にElectronだと「Chromiumしか考えなくていいから最新構文使い放題だぜ~:v:」となりがちなんで……(経験談)

おまけ:Webpackを通した使い方

プラグイン一覧の中にあるwebpack-obfucatorを使います。
このモジュールにはpluginとして使うか、loaderとして使うかの2種類の使い方があります。
自作モジュールと他人のモジュールを別々のjsファイルに纏めるならplugin、全部一つのjsファイルにまとめるならloaderがいいということのようです。複数バンドルに分けるような人はWebpackマスターでわざわざここ見る必要ないかと思うので、loaderを使っていきます。
さてwebpack.config.jsのloaderの項目に加えればいいのは想像つくかと思うのですが、問題はTypeScriptの場合です。この場合.tsのファイルにts-loaderとobfuscator-loaderを2つ通さないといけないのでWebpackに慣れてないと迷うかと思います。webpackのリファレンス見てもらうのが一番いいんですが、エイリアスやら省略記法多すぎて頭こんがらがってきたので、下記に一番無難そうな例を置いときます。

const webpackObfuscator = require('webpack-obfuscator');

  module: {
    rules: [
      {
        enforce: 'post',
        test: /\.ts$/,
        use: [
          {
            loader: webpackObfuscator.loader,
            options: //オプションのオブジェクト
          }
        ],
        exclude: /node_modules/
      },
      {
        test: /\.ts$/,
        use: [
          { loader: 'ts-loader' }
        ],
        exclude: /node_modules/
      }
    ]
  }

selfDefending設定のこともあり、基本的に一番最後に通すことが推奨されています。rulesの配列内の後ろから順に処理されるので、一番先頭に置いておくのもいいのですが、enforce:'post'を設定することで最後に処理するよう強制することができます。
あと「ts-loader通した後の処理だから、testプロパティは.tsか.js、どっちを対象にすればいいのかな?」と迷うかと思いますが、ここは.tsのままで大丈夫です。

参考

JavaScript難読化ツールの紹介と比較
ウェブアプリをソースごとパクる業者に対する対策
JavaScript の便利な難読化ツール「JavaScript Obfuscator Tool」
Nuxt.js プロジェクトで JavaScript を難読化

ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
コメント
この記事にコメントはありません。
あなたもコメントしてみませんか :)
すでにアカウントを持っている方は
ユーザーは見つかりませんでした