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

Ключ для Bytecode Array Encoding

Вынос ключа для Bytecode Array Encoding

Pro

Задайте собственный ключ шифрования байт-кода VM через vmBytecodeArrayEncodingKey и передавайте его обратно во время выполнения через геттер ключа - храните его вне бандла, читайте из хранилища на клиенте или получайте со своего бэкенда.

Смотреть

Obfuscator.io Async Executor: Async Bytecode Key Getter and Async-Only VM Virtualization

Смотреть на YouTube

Obfuscator.io Custom Bytecode Key: Compile-Time Key and Runtime Key Getter (Cookie, Fetch, Decoy Key)

Смотреть на YouTube

Что делают эти опции

vmBytecodeArrayEncoding шифрует массив байт-кода VM, чтобы он не лежал в результате открытым текстом. По умолчанию ключ шифрования выводится из окружения и восстанавливается на клиенте, так что вам не приходится с ним работать. Это удобно, но ключевой материал всё равно находится в бандле.

Две опции позволяют вынести ключ из бандла и управлять им самостоятельно:

  • vmBytecodeArrayEncodingKey - ключ, который вы задаёте во время компиляции. Если он задан, он используется вместо ключа по умолчанию, выводимого из окружения, и не встраивается в обфусцированный результат.
  • vmBytecodeArrayEncodingKeyGetter - JavaScript-выражение, которое возвращает тот же ключ во время выполнения. Оно встраивается дословно и вычисляется в браузере при загрузке обфусцированного кода.

Смысл в разделении: поскольку ключа нет в коде, чисто статический анализ бандла не может его восстановить. Во время выполнения ключ всё равно должен быть доступен, иначе код не заработает, поэтому по-настоящему секретным он не является, но вы сами решаете, откуда он берётся и кто может его увидеть.

Эти две опции работают в паре, и обфускатор это проверяет: если задать одну без другой, при сборке возникает ошибка валидации (у одного vmBytecodeArrayEncodingKey нет способа получить ключ во время выполнения, а у одного геттера нет ключа времени компиляции, с которым он должен совпасть). Кроме того, они ничего не делают, если массив байт-кода на самом деле не шифруется, поэтому должен быть включён и vmBytecodeArrayEncoding: true - или vmSelfDefending: true, который внутренне принудительно включает vmBytecodeArrayEncoding. Задайте оба ключа вместе с одной из этих опций.

Как сочетаются два ключа

Ваш ключ никогда не используется сам по себе: с обеих сторон он смешивается с внутренним ключом, которым управляет обфускатор:

  • Во время компиляции. vmBytecodeArrayEncodingKey объединяется с внутренним ключом, который выводит обфускатор, и массив байт-кода кодируется получившимся смешанным ключом.
  • Во время выполнения. Значение, которое возвращает ваш vmBytecodeArrayEncodingKeyGetter, объединяется с тем же внутренним ключом, восстановленным на клиенте из различных факторов среды выполнения, чтобы декодировать байт-код.

Поскольку обе стороны смешивают ваш ключ с внутренним, геттер должен возвращать точно ту же строку, которую вы передали в vmBytecodeArrayEncodingKey. Ни одной из частей по отдельности недостаточно: ваш ключ без внутреннего не может декодировать байт-код, а внутренний ключ бесполезен без вашего. Поэтому код на самом деле защищает именно контроль над тем, кто получает ваш ключ.

Передача ключа во время выполнения

По умолчанию геттер синхронный: выражение должно вернуть ключ сразу при загрузке обфусцированного кода. Читайте его из любого источника, который уже есть на клиенте: cookie, localStorage, глобальной переменной или DOM-элемента, внедрённого сервером.

JavaScript

Ключ должен существовать до запуска обфусцированного кода:

JavaScript

Другие синхронные источники работают так же - выберите тот, который ваше приложение уже заполняет:

JavaScript

Не храните ключ в том же файле или скрипте, что и обфусцированный код. Встраивание его туда лишает всё это смысла: статический анализ бандла восстановит и код, и ключ. Храните ключ в отдельном источнике, а vmBytecodeArrayEncodingKey для компиляции подставляйте из переменной окружения или секрета, а не коммитьте в репозиторий.

Получение ключа с бэкенда (асинхронно)

Требует vmAsyncExecutor · v7.3.0+

Синхронный геттер может прочитать только то, что уже есть на клиенте. Чтобы получать ключ с вашего сервера и иметь возможность закрыть его аутентификацией и отозвать, геттер должен быть асинхронным, а для этого нужен vmAsyncExecutor. При включённом Async Executor геттер может вернуть Promise, и VM дождётся его перед запуском.

Включение vmAsyncExecutor также сужает то, что виртуализируется: в этом режиме в VM компилируются только самые внешние async-функции, а синхронный код не виртуализируется (остальная обфускация к нему всё равно применяется). Если ваша программа в основном синхронная, оберните код, который нужно защитить, в async-функцию, чтобы он всё равно был покрыт; полное правило описано в разделе vmAsyncExecutor.

JavaScript

Геттер, возвращающий Promise, требует vmAsyncExecutor. Это невозможно проверить во время сборки, поэтому такой геттер при выключенном vmAsyncExecutor ломается во время выполнения.

На сервере решайте, какой ключ вернуть, исходя из того, чему доверяет ваше приложение: проверенной сессии, проверке лицензии и так далее. Одни только Origin или Referer не аутентифицируют вызывающую сторону, а запросы GET с того же источника могут не содержать Origin. Хитрость в том, чтобы не отклонять недоверенных клиентов, а возвращать им неверный ключ (VM_DECOY_KEY ниже). Тогда защищённый код ломается сам по себе (ошибки, неверные результаты или зависшая страница). Это незаметнее, чем явный 401, который прямо подсказывает злоумышленнику, что именно нужно обойти.

Отключите кеширование ответа, чтобы ключ одного клиента никогда не попал к другому. Привязывайте ключи к нужной версии сборки и развёртывайте ключи вместе с бандлами. Клиент, получивший настоящий ключ, всё равно может изучить его во время выполнения.

JavaScript

Отдавайте с этого эндпоинта точно ту же строку, которую вы передали в vmBytecodeArrayEncodingKey при сборке. Геттер выше запрашивает относительный URL (/api/vm-key), поэтому копия бандла, размещённая на другом источнике (origin), запрашивает /api/vm-key у этого источника и поэтому никогда не получает ваш ключ; ключ-приманку получает тот, кто всё-таки обращается к вашему эндпоинту, но без доверенной сессии (неаутентифицированный запрос, украденный бандл, проксируемый через ваш источник). В любом случае настоящий ключ не приходит, и защищённый код не выполняется.

Что считать «допустимым», целиком зависит от приложения: аутентифицированная сессия, подписанная лицензия или любое их сочетание. Что бы ни выдавало ключ, оберните результат в Promise, и Async Executor дождётся его перед запуском VM.

Если ключ не совпадает

Обфусцированный код работает, только если геттер возвращает точно тот же ключ, что использовался при обфускации. Если ключи различаются - или геттер возвращает undefined, null или пустую строку, - код ломается во время выполнения: выдаёт неверные результаты, выбрасывает обычную ошибку выполнения или перестаёт отвечать.

Отдельного сообщения об ошибке, связанного с ключом, намеренно нет: неверный ключ неотличим от любого другого сбоя во время выполнения. Поэтому если VM-защищённый бандл падает, возвращает неверные результаты или зависает только после включения этой опции, в первую очередь проверьте путь получения ключа: что геттер срабатывает на странице, возвращает непустую строку и возвращает то же значение, с которым вы собирали код.