Получение ключа массива байт-кода с бэкенда
Держите ключ расшифровки байт-кода VM вне клиента: запрашивайте его с бэкенда при загрузке через асинхронный геттер, защищённый аутентификацией, чтобы украденный бандл был бесполезен.
Проблема
vmBytecodeArrayEncoding шифрует байт-код VM ключом. Если этот ключ поставляется
внутри бандла, у любого, у кого есть файл, есть всё необходимое для расшифровки. vmBytecodeArrayEncodingKeyGetter
позволяет ключу находиться в другом месте и вычисляться во время выполнения — но синхронный геттер может прочитать лишь
то, что уже присутствует на клиенте (глобальную переменную, cookie, localStorage). Чтобы получить ключ со своего
сервера — и тем самым защитить его аутентификацией и иметь возможность отозвать — геттер должен быть асинхронным.
Решение
Включите vmAsyncExecutor (v7.3.0+). С асинхронным исполнителем геттер ключа может
возвращать Promise, поэтому он может fetch-ить ключ с вашего бэкенда до запуска VM. Три опции работают вместе:
vmBytecodeArrayEncoding: true— шифровать массив байт-кода.vmBytecodeArrayEncodingKey— ключ, используемый на этапе компиляции (хранится на вашем сервере, не в бандле).vmBytecodeArrayEncodingKeyGetter— JS-выражение, возвращающее тот же ключ во время выполнения. При включённомvmAsyncExecutorоно может возвращатьPromise; без него геттер должен возвращать ключ синхронно.
Конфигурация обфускации на клиенте
JavaScriptObfuscator.obfuscate(sourceCode, {
vmObfuscation: true,
vmAsyncExecutor: true,
vmBytecodeArrayEncoding: true,
vmBytecodeArrayEncodingKey: process.env.VM_KEY, // e.g. 'mySecretKey123'
vmBytecodeArrayEncodingKeyGetter:
'fetch("/api/vm-key", { credentials: "include" }).then((res) => res.text())'
});
Выражение геттера встраивается дословно и вычисляется в браузере. Держите ключ компиляции
vmBytecodeArrayEncodingKey вне клиентского репозитория — подставляйте его из переменной окружения или секрета на этапе
сборки и отдавайте ровно ту же строку из эндпоинта ниже.
Как работают два ключа
Ваш ключ никогда не используется сам по себе — с обеих сторон он смешивается с внутренним ключом, которым управляет обфускатор:
- Этап компиляции.
vmBytecodeArrayEncodingKeyкомбинируется с внутренним ключом, который выводит обфускатор, и массив байт-кода кодируется получившимся смешанным ключом. - Во время выполнения. Значение, к которому разрешается ваш
vmBytecodeArrayEncodingKeyGetter— возвращённое вашим сервером, — комбинируется с тем же внутренним ключом, восстановленным на клиенте из различных факторов среды выполнения, чтобы декодировать байт-код.
Поскольку обе стороны смешивают ваш ключ с внутренним, геттер должен разрешаться в точно ту же строку, которую вы
передали как vmBytecodeArrayEncodingKey. Ни одной части по отдельности недостаточно: ваш ключ без внутреннего не может
декодировать байт-код, а внутренний бесполезен без вашего — именно поэтому отдача вашего ключа только аутентифицированным
вызывающим делает украденный бандл бесполезным.
Серверная сторона
Эндпоинт решает, какой ключ вернуть, на основе того, чему доверяет ваше приложение, — валидной сессии, ожидаемого
Origin или Referer, проверки лицензии и т. д. Хитрость: вместо отклонения недоверенных вызывающих верните неверный
ключ. Байт-код тогда декодируется в мусор, и защищённый код падает сам по себе, что незаметнее очевидного 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
);
});
