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