難読化された複数ファイル間での識別子の衝突を避ける
VM 難読化した複数のファイルが同じバンドルに入ると、同じトップレベル識別子を宣言してしまい、パース時にページがクラッシュすることがあります。その原因と対処法を説明します。
症状
ビルドは問題なく通るのに、アプリが起動する前のパース時にブラウザーがエラーを投げます。
エラーに出た名前をいくつのチャンクが宣言しているかを確認するには、その名前の宣言を含むファイルを数えます。
-l は一致したファイルを 1 回ずつ列挙し、-w は単語全体にのみ一致するため、vmdX は数えられません。var の宣言を繰り返すこと自体はエラーではないため、var は除外しています。結果が 1 より大きければ、その名前が複数のチャンクで宣言されています。vmd はエラーメッセージに出た名前に置き換えてください。
識別子の衝突が問題になるのは、宣言が同じスコープを共有する場合だけです。別々のモジュールやクロージャで同じ名前が繰り返されていても衝突の証拠にはなりません。証拠になるのは上記のパースエラーです。
発生する理由
VM プリセットは mangled-shuffled 識別子名ジェネレーターを使用します。このジェネレーターは小さなアルファベットをシャッフルされた順序でたどり、VM 難読化では、リネームされたすべてのグローバルに vm プレフィックス(設定した identifiersPrefix の前に追加されるため、checkout_ は vm_checkout_... になります)が付きます。シャッフルされた順序はプロセスごとにキャッシュされるため、同じプロセスで難読化されたファイルや、同じ固定の seed で難読化されたファイルは同じ順序から名前を取ります。そのため、各ファイルの最初のグローバルは同じ名前になり、2 番目のグローバルも別の同じ名前になる、というように続きます。
各ファイルは独立して難読化され、ジェネレーターはファイルごとに順序の先頭からやり直します。2 つのファイルが同じスコープに入ると、名前が衝突します。トップレベルの let vmd 宣言が 2 つ同じスコープに置かれ、パーサーは 2 つ目を拒否します。
VM 難読化された各ファイルは、サフィックスの付かないトップレベルのランタイム名を 1 つ宣言します。それだけで衝突が起こりえます。renameGlobals: true を使うと状況は大きく悪化します。すべてのトップレベル宣言が短い vm* 名になり、ランタイム自身の名前も一意のサフィックスを失います。
対処法
まずバンドルしてから、そのバンドルを一度だけ難読化する方法をおすすめします。ファイルを個別に難読化し、かつスクリプトがグローバルスコープを共有する場合は、次のいずれかを選んでください。
randomIdentifiersPrefixを有効にする(推奨)難読化の実行ごとに、すべてのグローバル識別子の先頭にランダムなプレフィックスが付きます(VM 難読化ではデフォルトの
vmプレフィックスを置き換えます)。異なるファイルの名前が同じ名前空間を共有しなくなるため、プレフィックスを手作業で調整しなくても衝突がなくなります。ファイルごとに一意の
identifiersPrefixを設定する各ファイルを難読化するときに、異なるプレフィックスを手動で渡します(例えば、あるファイルには
identifiersPrefix: 'auth_'、別のファイルにはcheckout_)。効果はありますが、ファイルが多いと間違いが起きやすいため、上記のランダムなオプションをおすすめします。identifierNamesGeneratorをhexadecimalに切り替える16 進数の名前ははるかに大きなキー空間を使うため、2 つのファイルが同じ識別子を生成する可能性は大幅に下がります。ただし、一意性は保証されません。トレードオフとして、識別子が
mangled-shuffledの出力より長くなるため、バンドルがやや大きくなります。複数ファイルの一括処理 UI を使うか、バンドラープラグインを確認する
ダッシュボードでは、1 回の複数ファイルの一括処理(有料プラン)の中でファイルごとに異なるプレフィックスが付きます。別々の一括処理で難読化したファイル同士は衝突する可能性があります。バンドラープラグインを使う場合は、同じ動作をすると決めつけず、その動作を確認してください。
javascript-obfuscatorパッケージや API を自分で呼び出す場合は、プレフィックスを自分で指定する必要があります。
修正を確認するには、結合したアプリケーションを再ビルドして再読み込みし、SyntaxError が消えたことを確かめてください。以前のエラーメッセージに出た名前は、もう複数のチャンクで宣言されていないはずです。プレフィックスは生成されるすべての名前を変えるため、上の grep でわかるのはその特定の名前についてだけで、新しい名前についてはわかりません。
関連オプション
randomIdentifiersPrefix:ビルドごとのランダムなプレフィックス(推奨)identifiersPrefix:明示的なプレフィックス文字列identifierNamesGenerator:名前の形式(mangled、hexadecimalなど)
