Dokumentacja
/
Przepisy
/

Podstawianie szablonów po stronie serwera

Podstawianie szablonów po stronie serwera

Pro
v6.10.0+

Wstrzykuj wartości renderowane na serwerze (np. placeholdery w stylu szablonów Go `{{ .Field }}`) do kodu JavaScript zobfuskowanego przez VM, nie psując procesu obfuskacji.

Problem

Twój backend (Go, Rails, Django, PHP, …) serwuje plik JavaScript, którego zawartość musi być częściowo renderowana przy każdym żądaniu - endpoint API, lista flag funkcji, blob stanu początkowego, identyfikator builda, nonce. Obfuskujesz kod JS z vmObfuscation: true, ale silnik szablonów musi też podstawić placeholdery w zobfuskowanym wyniku po zakończeniu obfuskacji. reservedNames i reservedStrings pozostawiają placeholder widoczny w wyniku, dzięki czemu można go zastąpić. Bez nich placeholder w postaci ciągu zostaje wchłonięty przez kod bajtowy VM, a placeholder w postaci identyfikatora trafia do wyniku w kilku przepisanych miejscach, więc silnik szablonów albo nie znajduje nic do podstawienia, albo psuje wynik.

Rozważ wczytanie wartości jako danych

Jeśli to możliwe, w ogóle nie edytuj chronionego wyniku: wczytaj konfigurację uruchomieniową jako dane, zanim uruchomi się chroniony bundle. Trzymaj ją w osobnym skrypcie albo pobieraj z uwierzytelnionego endpointu. Chroniony wynik pozostaje wtedy nienaruszony, więc vmSelfDefending może zostać włączone. Dane dostarczone do przeglądarki są dla niej widoczne w każdym przypadku.

JavaScript

Stosuj poniższe wzorce, gdy wartości naprawdę trzeba podstawić w zobfuskowanym pliku.

Uwaga o przykładach w Go: wykonują one zwykłe zastąpienie tekstu (strings.ReplaceAll / strings.NewReplacer) w zobfuskowanym wyniku i same zajmują się escapowaniem, pokazanym przy każdym wzorcu. Nie są przepuszczane przez pakiet Go html/template - ten zastosowałby dodatkowo własne kontekstowe escapowanie JavaScriptu, escapując dane podwójnie. Jeśli renderujesz przez html/template, zrezygnuj z ręcznego escapowania i pozwól silnikowi wykonać je raz.

Którą opcję wybrać?

Wartość z serwera to…Użyj
Surowa wartość JS (tablica, obiekt, liczba, wartość logiczna - dowolna wartość JSON)Wzorzec 1 - reservedNames + placeholder w postaci identyfikatora
Ciąg znaków (typowy przypadek - wyrenderowany JSON, identyfikator builda, nonce)Wzorzec 2 - reservedStrings + placeholder w template literal
Ciąg znaków, gdy kod musi pozostać w ES5 albo otaczające API wymaga literału tekstowego w cudzysłowachWzorzec 3 - reservedStrings + placeholder w ciągu w cudzysłowach

Podstawianie po obfuskacji jest niezgodne z vmSelfDefending: true. Zobacz Uwagi o zgodności na końcu tego przepisu.

Wzorzec 1 - reservedNames z placeholderem w postaci identyfikatora

Najlepszy do: wstrzykiwania surowych wyrażeń JS (tablic, obiektów, liczb, …) bez żadnych problemów z escapowaniem cudzysłowów.

Wybierz charakterystyczny identyfikator, który nigdy nie zderzy się z prawdziwym kodem, odwołaj się do niego bezpośrednio i dodaj pasujące do niego wyrażenie regularne do reservedNames. Przy obfuskacji VM identyfikator trafia do tablicy zarezerwowanych wyrażeń i pojawia się w wyniku dosłownie, na przykład:

JavaScript

Zastąp identyfikator kompletnym wyrażeniem zserializowanym do JSON. Nie wklejaj wartości w cudzysłowach ani w backtickach - identyfikator nie znajduje się w literale tekstowym, więc wstrzyknięta wartość jest parsowana przez silnik JS jako zwykłe wyrażenie. Używaj zaufanego serializatora, escapuj <, gdy skrypt jest osadzony w HTML, i testuj wartości zawierające cudzysłowy, ukośniki wsteczne, podziały wierszy, backticki oraz ${...}.

JavaScript

Wzorzec 2 - reservedStrings z placeholderem w template literal

Najlepszy do: silników szablonów w stylu Go / Jinja, które wymagają ograniczników takich jak {{ .Field }}, a te akurat są poprawnym tekstem JS po otoczeniu backtickami.

Placeholder znajduje się w template literal z jedną częścią quasi i bez interpolacji. Przy obfuskacji VM trafia on do tablicy zarezerwowanych wyrażeń, która zachowuje w wyniku surową postać z backtickami.

Kod źródłowy

JavaScript

Opcje obfuskatora

UI

Aby zarezerwować placeholder {{.Config.FeatureFlags}} z powyższego kodu, dodaj to wyrażenie regularne w polu Reserved Strings - z pojedynczymi ukośnikami wstecznymi. UI zapisuje wartość dosłownie, więc inaczej niż w kodzie JS ukośniki wsteczne nie są podwajane:

Tekst

Pole Reserved Strings w UI obfuskatora z wyrażeniem regularnym wpisanym z pojedynczymi ukośnikami wstecznymi

API

JavaScript

Po obfuskacji

JavaScript

Placeholder zostaje zachowany bajt w bajt, razem z otaczającymi go backtickami.

Podstawianie po stronie serwera

Zastąp {{.Config.FeatureFlags}} ciągiem JSON zescapowanym na potrzeby template literal. Backticki nie wymagają escapowania wewnętrznych ", ale ukośnik wsteczny, backtick lub ${ w danych nadal zmieniłyby lub zakończyły literał, więc escapuj te trzy:

Kod

W czasie działania: JSON.parse(`{"newCheckout":true,"darkMode":false}`) - działa.

Dlaczego dla wartości tekstowych lepsze są template literals niż pojedyncze lub podwójne cudzysłowy? Backticki nie wymagają escapowania " w danych JSON. Ma to znaczenie, bo większość wartości renderowanych na serwerze to JSON, a JSON jest pełen podwójnych cudzysłowów. Przy placeholderze w podwójnych cudzysłowach musiałbyś escapować każdy wewnętrzny " (zobacz Wzorzec 3); przy backtickach escapujesz tylko rzadkie ukośniki wsteczne, backticki i ${.

Wzorzec 3 - reservedStrings z placeholderem w ciągu w cudzysłowach

Najlepszy do: kodu źródłowego, który musi pozostać w ES5 (bez template literals), albo sytuacji, w których otaczające API oczekuje zwykłego literału tekstowego.

Placeholder jest literałem tekstowym w pojedynczych lub podwójnych cudzysłowach. Przy obfuskacji VM trafia on do tablicy zarezerwowanych ciągów, emitowanej jako tablica JS zserializowana przez JSON.stringify - zawsze w podwójnych cudzysłowach, niezależnie od cudzysłowów w kodzie wejściowym.

Kod źródłowy

JavaScript

Opcje obfuskatora

UI

Aby zarezerwować placeholder {{.Page.Tags}} z powyższego kodu, dodaj to wyrażenie regularne w polu Reserved Strings

  • z pojedynczymi ukośnikami wstecznymi. UI zapisuje wartość dosłownie, więc inaczej niż w kodzie JS ukośniki wsteczne nie są podwajane:

Tekst

Pole Reserved Strings w UI obfuskatora z wyrażeniem regularnym wpisanym z pojedynczymi ukośnikami wstecznymi

API

JavaScript

Po obfuskacji

JavaScript

Podstawianie po stronie serwera

Ponieważ placeholder znajduje się w ciągu JS w podwójnych cudzysłowach, we wstrzykiwanych danych JSON trzeba zescapować ukośniki wsteczne i wewnętrzne znaki ":

Kod

Wynik:

JavaScript

W czasie działania: JSON.parse("[\"news\",\"tech\",\"release\"]") → ["news","tech","release"].

Jeśli zapomnisz o escapowaniu, przeglądarka zobaczy niezrównoważone cudzysłowy i rzuci SyntaxError. Wzorzec 2 całkowicie omija podwójne cudzysłowy dzięki backtickom.

Wiele placeholderów w jednym programie

Wszystkie trzy wzorce można łączyć. Jedno wyrażenie regularne w reservedStrings z alternatywą może dopasować każdy kształt placeholdera, jaki emituje Twój silnik szablonów - tutaj zarówno styl {{ .Field }}, jak i %{ .Field }:

JavaScript

W jednym kodzie źródłowym możesz mieszać placeholdery w postaci identyfikatorów (dla surowych wartości JS) i placeholdery tekstowe (dla renderowanego JSON) - wybieraj osobno dla każdego placeholdera, zależnie od tego, co serwer faktycznie wstrzyknie.

Uwagi o zgodności

vmSelfDefending psuje podstawianie po obfuskacji

VM Self Defending wykrywa każdą zmianę zobfuskowanego wyniku po jego zbudowaniu, w tym prawidłowe podstawienie szablonu, a chroniony kod odmawia wtedy uruchomienia.

Jeśli polegasz na podstawianiu szablonów po stronie serwera, ustaw vmSelfDefending: false. Pozostaw też wyłączone selfDefending: przy obfuskacji VM nie ma ono żadnego efektu, ale bez VM również zabrania jakiejkolwiek zmiany wyniku.

Wskazówki dotyczące placeholderów

  • Nie używaj tych samych placeholderów dla identyfikatorów i ciągów. Zarezerwowana nazwa, np. __TOKEN__, i zarezerwowany ciąg pasujący do __TOKEN__ opisują dwie różne ścieżki kodu (tablica zarezerwowanych wyrażeń i tablica zarezerwowanych ciągów). Używaj dla każdej z nich innego kształtu tekstu - na przykład konwencji UPPER_SNAKE dla placeholderów w postaci identyfikatorów i kształtu otoczonego ogranicznikami ({{ ... }}, %{...}, <<<...>>>) dla placeholderów tekstowych. Dzięki temu błąd w jednym wyrażeniu regularnym nie dopasuje po cichu drugiego rodzaju.
  • Wyrażenia regularne reservedStrings działają na surowych wartościach ciągów. Wyrażenie jest dopasowywane do wartości ciągu w czasie wykonania, a nie do tekstu źródłowego. \{\{[^}]+\}\} dopasowuje ciągi, które zawierają {{.something}} (lub dowolny inny kształt {{...}}). Jeśli placeholder może być otoczony dodatkową treścią ("prefix-{{.Field}}-suffix"), wyrażenie nadal pasuje, ale zachowany zostaje cały ciąg - zaplanuj podstawianie odpowiednio do tego.
  • Działa z każdym silnikiem szablonów. Choć przykłady używają składni Go, w integracji z javascript-obfuscator nie ma nic specyficznego dla Go. Zadziała wszystko, co potrafi zastąpić tekst w wyniku obfuskatora: Rails ERB, Django, krótkie tagi PHP, sed w potoku CI itd. Wybierz ograniczniki, które Twój silnik emituje naturalnie i które nie kolidują z prawdziwą składnią JS.