Документация
/
Рецепты
/

Получение ключа массива байт-кода с бэкенда

Получение ключа массива байт-кода с бэкенда

Pro
v7.3.0+

Держите ключ расшифровки байт-кода 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
    );
});