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

Подстановка значений в шаблон на стороне сервера

Подстановка значений в шаблон на стороне сервера

v6.10.0+

Внедряйте отрисованные на сервере значения (например, выражения Go html/template `{{ .Field }}`) в VM-обфусцированный JavaScript, не ломая конвейер обфускации.

Проблема

Ваш бэкенд (Go, Rails, Django, PHP, …) отдаёт файл JavaScript, содержимое которого должно частично отрисовываться для каждого запроса — эндпоинт API, список фича-флагов, блоб начального состояния, id сборки, nonce. Вы обфусцируете этот JS с vmObfuscation: true, но вам также нужно, чтобы шаблонизатор подставил плейсхолдеры в обфусцированном результате после завершения обфускации. Если плейсхолдеры вобраны в байт-код VM (поведение по умолчанию для строк и идентификаторов), шаблонизатору нечего подставлять.

Какую опцию выбрать?

Значение с сервера…Использовать
Сырое JS-выражение (литерал массива, объект, число, булево значение, вызов функции)reservedNames + плейсхолдер-идентификатор
Строка, и вы контролируете разделители шаблонизатораreservedStrings + плейсхолдер-шаблонный литерал
Строка, и шаблонизатор навязывает определённые разделители (например, Go {{ .Field }})reservedStrings + плейсхолдер-строка или шаблонный литерал

Паттерн 1 — reservedNames с плейсхолдером-идентификатором

Лучше всего для: внедрения сырых JS-выражений (массивы, объекты, числа, …) без каких-либо забот об экранировании кавычек.

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

Исходник

(function () {
    var initialState = __INITIAL_STATE__;
    bootstrap(initialState);
})();

Опции обфускатора

JavaScriptObfuscator.obfuscate(source, {
    vmObfuscation: true,
    reservedNames: ['^__INITIAL_STATE__$']
});

После обфускации

Где-то в результате вы найдёте массив зарезервированных выражений, похожий на:

let _r = [__INITIAL_STATE__, /* …other entries… */];

Токен __INITIAL_STATE__ переживает переименование идентификаторов и не вбирается в байт-код VM.

Подстановка на стороне хоста (пример на Go)

tpl := template.Must(template.New("obf.js").Parse(obfuscated))
tpl.Execute(w, map[string]any{
    "InitialState": map[string]any{"user": "alice", "theme": "dark"},
})

…где ваш шаблон Go заменяет токен на сырое JS-выражение:

{{ `__INITIAL_STATE__` }} → {{ .InitialState | toJSON }}

Результат:

let _r = [{"user":"alice","theme":"dark"}, /* … */];

Ни кавычек, ни экранирования — внедрённое значение разбирается движком JS как обычное выражение.

Паттерн 2 — reservedStrings с плейсхолдером-шаблонным литералом

Лучше всего для: шаблонизаторов в стиле Go / Jinja, которым нужны разделители вроде {{ .Field }}, оказывающиеся валидным JS-текстом при обёртывании в обратные кавычки.

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

Исходник

(function () {
    var featureFlags = JSON.parse(`{{.Config.FeatureFlags}}`);
    applyFlags(featureFlags);
})();

Опции обфускатора

UI

Чтобы зарезервировать плейсхолдер {{.Config.FeatureFlags}} из исходника выше, добавьте это регулярное выражение в поле Reserved Strings — с одинарными обратными слешами. Интерфейс хранит значение дословно, поэтому, в отличие от JS-кода, обратные слеши не удваиваются:

\{\{[^}]+\}\}

Поле Reserved Strings в интерфейсе обфускатора с введённым регулярным выражением с одинарными обратными слешами

API

JavaScriptObfuscator.obfuscate(source, {
    vmObfuscation: true,
    reservedStrings: ['\\{\\{[^}]+\\}\\}']
});

После обфускации

let _r = [`{{.Config.FeatureFlags}}`, /* … */];

Плейсхолдер сохраняется байт в байт, включая окружающие обратные кавычки.

Подстановка на стороне хоста

Замените {{.Config.FeatureFlags}} на валидную JSON-строку — во время выполнения она окажется внутри обратных кавычек, которые не требуют экранирования внутренних кавычек:

flagsJSON, _ := json.Marshal(config.FeatureFlags) // e.g. {"newCheckout":true,"darkMode":false}
out := strings.ReplaceAll(obfuscated, "{{.Config.FeatureFlags}}", string(flagsJSON))

Во время выполнения: JSON.parse(`{"newCheckout":true,"darkMode":false}`) — работает.

Паттерн 3 — reservedStrings с плейсхолдером-строкой в кавычках

Лучше всего для: исходного кода, который должен оставаться в ES5 (без шаблонных литералов), или случаев, когда окружающий API ожидает обычный строковый литерал.

Плейсхолдер — строковый литерал в одинарных или двойных кавычках. При VM-обфускации он направляется через массив зарезервированных строк, который выводится как сериализованный через JSON.stringify JS-массив — всегда в двойных кавычках независимо от стиля кавычек на входе.

Исходник

(function () {
    var tags = JSON.parse("{{.Page.Tags}}");
    renderTags(tags);
})();

Опции обфускатора

UI

Чтобы зарезервировать плейсхолдер {{.Page.Tags}} из исходника выше, добавьте это регулярное выражение в поле Reserved Strings — с одинарными обратными слешами. Интерфейс хранит значение дословно, поэтому, в отличие от JS-кода, обратные слеши не удваиваются:

\{\{[^}]+\}\}

Поле Reserved Strings в интерфейсе обфускатора с введённым регулярным выражением с одинарными обратными слешами

API

JavaScriptObfuscator.obfuscate(source, {
    vmObfuscation: true,
    reservedStrings: ['\\{\\{[^}]+\\}\\}']
});

После обфускации

var _rs = ["{{.Page.Tags}}", /* … */];

Подстановка на стороне хоста

Поскольку плейсхолдер находится внутри JS-строки в двойных кавычках, во внедряемой полезной нагрузке JSON внутренние символы " должны быть экранированы как \":

raw, _ := json.Marshal(page.Tags) // e.g. ["news","tech","release"]
// Escape " for embedding inside a JS double-quoted string.
escaped := strings.ReplaceAll(string(raw), `"`, `\"`)
out := strings.ReplaceAll(obfuscated, "{{.Page.Tags}}", escaped)

Итоговый результат:

var _rs = ["[\"news\",\"tech\",\"release\"]", /* … */];

Во время выполнения: JSON.parse("[\"news\",\"tech\",\"release\"]")["news","tech","release"].

Если вы забудете об экранировании, браузер увидит несбалансированные кавычки и бросит SyntaxError. Паттерн 2 полностью обходит это, используя обратные кавычки.

Несколько плейсхолдеров в одной программе

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

JavaScriptObfuscator.obfuscate(source, {
    vmObfuscation: true,
    reservedNames: ['^__INITIAL_STATE__$'],
    reservedStrings: ['\\{\\{[^}]+\\}\\}']
});

Вы можете смешивать плейсхолдеры-идентификаторы (для сырых JS-значений) и плейсхолдеры-строки (для отрисованного JSON) в одном исходнике — выбирайте под каждый плейсхолдер по тому, что сервер действительно внедрит.

Замечания о совместимости

  • Не переиспользуйте плейсхолдеры между идентификаторами и строками. Зарезервированное имя вроде __TOKEN__ и зарезервированная строка, соответствующая __TOKEN__, описывают два разных пути в коде (массив зарезервированных выражений против массива зарезервированных строк). Используйте для каждого различающиеся текстовые формы — например, соглашение UPPER_SNAKE для плейсхолдеров-идентификаторов и форму, обёрнутую разделителями ({{ ... }}, %{...}, <<<...>>>), для плейсхолдеров-строк. Так ошибка в одном регулярном выражении не сможет незаметно совпасть с другим.
  • Регулярные выражения reservedStrings применяются к сырым строковым значениям. Регулярное выражение сопоставляется со значением строки во время выполнения, а не с текстом исходника. \{\{[^}]+\}\} соответствует строкам, которые содержат {{.something}} (или любую другую форму {{...}}). Если ваш плейсхолдер может быть обёрнут дополнительным содержимым ("prefix-{{.Field}}-suffix"), регулярное выражение всё равно совпадёт, но сохранится вся строка целиком — планируйте подстановку с учётом этого.
  • Работает с любым шаблонизатором. Хотя в примерах используется синтаксис Go text/template, ничто в интеграции с javascript-obfuscator не специфично для Go. Подойдёт всё, что может выполнить замену на уровне строк в результате обфускатора: Rails ERB, Django, короткие теги PHP, sed в CI-конвейере и т. д. Выбирайте разделители, которые ваш движок выдаёт естественно и которые не сталкиваются с настоящим синтаксисом JS.