ドキュメント
/

トラブルシューティング

/

識別子の衝突を避ける

難読化された複数ファイル間での識別子の衝突を避ける

VM 難読化した複数のファイルが同じバンドルに入ると、同じトップレベル識別子を宣言してしまい、パース時にページがクラッシュすることがあります。その原因と対処法を説明します。

症状

ビルドは問題なく通るのに、アプリが起動する前のパース時にブラウザーがエラーを投げます。

Text

エラーに出た名前をいくつのチャンクが宣言しているかを確認するには、その名前の宣言を含むファイルを数えます。

Text

-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_)。効果はありますが、ファイルが多いと間違いが起きやすいため、上記のランダムなオプションをおすすめします。

    JavaScript

  • identifierNamesGenerator を hexadecimal に切り替える

    16 進数の名前ははるかに大きなキー空間を使うため、2 つのファイルが同じ識別子を生成する可能性は大幅に下がります。ただし、一意性は保証されません。トレードオフとして、識別子が mangled-shuffled の出力より長くなるため、バンドルがやや大きくなります。

  • 複数ファイルの一括処理 UI を使うか、バンドラープラグインを確認する

    ダッシュボードでは、複数ファイルの一括難読化(有料プランで利用可能)を使うとファイルごとに異なるプレフィックスが付きます。バンドラープラグインを使う場合は、同じ動作をすると決めつけず、その動作を確認してください。npm API で難読化を手動で組み込む場合は、プレフィックスを自分で指定する必要があります。

修正を確認するには、結合したアプリケーションを再ビルドして再読み込みし、SyntaxError が消えたことを確かめてください。以前のエラーメッセージに出た名前は、もう複数のチャンクで宣言されていないはずです。プレフィックスは生成されるすべての名前を変えるため、上の grep でわかるのはその特定の名前についてだけで、新しい名前についてはわかりません。

関連オプション