Podstawianie szablonów po stronie serwera
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.
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łowach | Wzorzec 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:
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 ${...}.
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
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:

API
Po obfuskacji
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:
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
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:

API
Po obfuskacji
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 ":
Wynik:
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 }:
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 konwencjiUPPER_SNAKEdla 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
reservedStringsdział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.
