ドキュメント
/
レシピ
/

バイトコード配列エンコーディングキー

バイトコード配列エンコードキーの外部化

Pro

vmBytecodeArrayEncodingKey で独自の VM バイトコード暗号化キーを指定し、キーゲッターを介して実行時にそれを返します — バンドルの外に保持し、クライアントストレージから読み取り、あるいはバックエンドから取得します。

これらのオプションの役割

vmBytecodeArrayEncoding は VM バイトコード配列を暗号化し、出力内に平文として 存在しないようにします。デフォルトでは、暗号化キーは環境から導出されクライアント側で再構築されるため、キーを 一切扱う必要がありません。これは便利ですが、キーの素材は依然としてバンドル内に残ります。

2 つのオプションを使えば、キーをバンドルから取り出し、自分で制御できます。

  • vmBytecodeArrayEncodingKeyコンパイル時に指定するキーです。設定すると、デフォルトの環境由来キーの代わりに 使用され、難読化された出力には埋め込まれません
  • vmBytecodeArrayEncodingKeyGetter実行時にその同じキーを返す JavaScript 式です。そのまま埋め込まれ、 難読化されたコードが読み込まれるときにブラウザ内で評価されます。

要点は分離です。キーがコード内に存在しないため、バンドルを純粋に静的にスキャンしてもキーを復元できません。 コードを実行するには実行時にキーが存在している必要があるため、真に秘密というわけではありませんが、キーがどこから 来て誰が見られるかを自分で決められます。

2 つのキーが組み合わさる仕組み

あなたのキーが単独で使われることはありません。両側で、難読化ツールが管理する内部キーと混合されます。

  • コンパイル時。 vmBytecodeArrayEncodingKey は難読化ツールが導出する内部キーと結合され、その結果得られた 混合キーでバイトコード配列がエンコードされます。
  • 実行時。 vmBytecodeArrayEncodingKeyGetter が解決する値は、さまざまな実行時要因からクライアント側で 再構築された同じ内部キーと結合され、バイトコードをデコードします。

両側であなたのキーと内部キーが混合されるため、ゲッターは vmBytecodeArrayEncodingKey として渡したまったく同じ 文字列に解決される必要があります。どちらか一方だけでは不十分です。内部キーのないあなたのキーではバイトコードを デコードできず、あなたのキーのない内部キーも役に立ちません。だからこそ、誰があなたのキーを受け取るかを制御する ことが、実際にコードを保護することになるのです。

実行時にキーを供給する

デフォルトでは、ゲッターは同期的です。難読化されたコードが読み込まれるとき、式は即座にキーを返さなければ なりません。すでにクライアント上に存在する任意のソース(Cookie、localStorage、グローバル変数、サーバーが 注入した DOM 要素など)から読み取ってください。

JavaScriptObfuscator.obfuscate(sourceCode, {
    vmObfuscation: true,
    vmBytecodeArrayEncoding: true,
    vmBytecodeArrayEncodingKey: process.env.VM_KEY,       // e.g. 'mySecretKey123'
    vmBytecodeArrayEncodingKeyGetter: "window.__VM_KEY__" // returns the key at runtime
});

キーは難読化されたコードが実行される前に存在していなければなりません。

// Set by a different script, a server-injected inline script, etc.
window.__VM_KEY__ = 'mySecretKey123';

他の同期的なソースも同じように機能します。アプリがすでに設定しているものを選んでください。

// From a cookie
vmBytecodeArrayEncodingKeyGetter: "document.cookie.match(/vmKey=([^;]+)/)?.[1]"

// From localStorage
vmBytecodeArrayEncodingKeyGetter: "localStorage.getItem('vmKey')"

// From a server-injected meta tag
vmBytecodeArrayEncodingKeyGetter: "document.querySelector('meta[name=\"vm-key\"]').content"

// From a nested object
vmBytecodeArrayEncodingKeyGetter: "window.config.encryption.key"

バックエンドからキーを取得する(非同期)

vmAsyncExecutor が必要 · v7.3.0+

同期的なゲッターは、すでにクライアント上にあるものしか読み取れません。サーバーからキーを取得して、認証の 背後でゲートを設けたり取り消したりできるようにするには、ゲッターを非同期にする必要があり、そのためには vmAsyncExecutor が必要です。非同期エグゼキュータを有効にすると、ゲッターは Promise を返すことができ、VM は実行前にそれを待機します。

JavaScriptObfuscator.obfuscate(sourceCode, {
    vmObfuscation: true,
    vmAsyncExecutor: true,
    vmBytecodeArrayEncoding: true,
    vmBytecodeArrayEncodingKey: process.env.VM_KEY,       // kept on your server, not in the bundle
    vmBytecodeArrayEncodingKeyGetter:
        'fetch("/api/vm-key", { credentials: "include" }).then((res) => res.text())'
});

サーバー側では、アプリケーションが信頼するもの(有効なセッション、期待される OriginReferer、ライセンス チェックなど)に基づいて、どのキーを返すかを決定します。工夫すべき点は、信頼できない呼び出し元を拒否する代わりに、 間違ったキーを返すことです。そうするとバイトコードはゴミにデコードされ、保護されたコードが自ら失敗します。 これは、攻撃者に何を回避すればよいかを正確に教えてしまう明白な 401 よりもステルス性が高い方法です。

// Express example — the exact checks depend on your app
app.get('/api/vm-key', (req, res) => {
    const origin = req.get('origin');
    const trusted =
        req.session?.user &&                       // a valid session, and
        origin === 'https://app.example.com';      // the expected production origin

    res.type('text/plain').send(
        // Real key for valid users; a decoy for everyone else
        // (no session, or a localhost / unexpected origin).
        trusted ? process.env.VM_KEY : process.env.VM_DECOY_KEY
    );
});

このエンドポイントからは、ビルド時に vmBytecodeArrayEncodingKey として渡したものとまったく同じ文字列を返して ください。あなたの環境外で実行されるバンドルのコピーはおとりキーを受け取り、何にもデコードされず、動作しなく なります。

キーが一致しない場合

難読化されたコードは、ゲッターが難読化時に使用したものとまったく同じキーを返す場合にのみ動作します。キーが 異なる場合、あるいはゲッターが undefinednull、または空文字列を返す場合、復号は誤ったキーストリームを生成し、 コードは実行時にゴミ出力または通常のランタイムエラーで失敗します。

意図的に、キー固有の明確なエラーメッセージは用意されていません。失敗したキーは、他のランタイム障害と区別が つきません。そのため、このオプションを使い始めてはじめて VM で保護されたバンドルが例外をスローする場合は、まず キーのパスを確認してください。ゲッターがページ上で解決すること、空でない文字列を返すこと、そしてビルド時に使った ものと同じ値を返すことを確認します。