Template-Substitution auf Host-Seite
Fügen Sie serverseitig gerenderte Werte (z. B. Platzhalter im Stil von Go-Templates wie `{{ .Field }}`) in VM-obfuskiertes JavaScript ein, ohne die Obfuskierungs-Pipeline zu beschädigen.
Das Problem
Ihr Backend (Go, Rails, Django, PHP, …) liefert eine JavaScript-Datei aus, deren Inhalt pro Anfrage teilweise gerendert
werden muss: ein API-Endpunkt, eine Liste von Feature-Flags, ein Blob mit dem Anfangszustand, eine Build-ID, eine Nonce. Sie
obfuskieren das JS mit vmObfuscation: true, brauchen aber zusätzlich die Template-Engine, um Platzhalter in der obfuskierten
Ausgabe nach Abschluss der Obfuskierung zu ersetzen. reservedNames und reservedStrings halten einen Platzhalter in der
Ausgabe sichtbar, damit er ersetzt werden kann. Ohne sie wird ein String-Platzhalter in den VM-Bytecode aufgenommen und ein
Bezeichner-Platzhalter an mehreren umgeschriebenen Stellen ausgegeben, sodass die Template-Engine entweder nichts zu ersetzen
findet oder die Ausgabe beschädigt.
Laden Sie die Werte besser als Daten
Vermeiden Sie nach Möglichkeit, die geschützte Ausgabe überhaupt zu bearbeiten: Laden Sie die Laufzeitkonfiguration als Daten, bevor das
geschützte Bundle läuft. Halten Sie sie in einem separaten Skript oder rufen Sie sie von einem authentifizierten Endpunkt ab. So bleibt die
geschützte Ausgabe unverändert, und vmSelfDefending kann aktiviert bleiben. An einen Browser ausgelieferte Daten sind für diesen Browser
ohnehin sichtbar.
Verwenden Sie die folgenden Muster, wenn die Werte tatsächlich in die obfuskierte Datei eingesetzt werden müssen.
Ein Hinweis zu den Go-Beispielen: Sie führen eine einfache String-Ersetzung (strings.ReplaceAll / strings.NewReplacer)
an der obfuskierten Ausgabe durch und übernehmen das Escaping selbst, wie bei jedem Muster gezeigt. Sie laufen nicht durch
Gos Paket html/template - das würde zusätzlich sein eigenes kontextabhängiges JavaScript-Escaping anwenden und die
Nutzdaten doppelt escapen. Wenn Sie doch über html/template rendern, lassen Sie das manuelle Escaping weg und überlassen
Sie das einmalige Escaping der Engine.
Welche Option wähle ich?
| Der Serverwert ist … | Verwenden Sie |
|---|---|
| ein roher JS-Wert (Array, Objekt, Zahl, Boolean - jeder JSON-Wert) | Muster 1 - reservedNames + Bezeichner-Platzhalter |
| ein String (der häufige Fall - gerendertes JSON, eine Build-ID, eine Nonce) | Muster 2 - reservedStrings + Template-Literal-Platzhalter |
| ein String, wobei der Code ES5 bleiben muss oder die umgebende API ein String-Literal in Anführungszeichen erwartet | Muster 3 - reservedStrings + Platzhalter als String in Anführungszeichen |
Eine Ersetzung nach der Obfuskierung ist nicht kompatibel mit vmSelfDefending: true. Siehe
Hinweise zur Kompatibilität am Ende dieses Rezepts.
Muster 1 - reservedNames mit einem Bezeichner-Platzhalter
Am besten geeignet für: das Einfügen roher JS-Ausdrücke (Arrays, Objekte, Zahlen, …) ganz ohne Sorgen um das Escaping von Anführungszeichen.
Wählen Sie einen markanten Bezeichner, der nie mit echtem Code kollidiert, referenzieren Sie ihn direkt und tragen Sie einen
passenden regulären Ausdruck in reservedNames ein. Unter VM-Obfuskierung wird der Bezeichner über das Array der reservierten
Ausdrücke geleitet und erscheint unverändert in der Ausgabe, zum Beispiel:
Ersetzen Sie den Bezeichner durch einen vollständigen, per JSON serialisierten Ausdruck. Setzen Sie den Wert nicht in Anführungszeichen
oder Backticks: Der Bezeichner steht in keinem String-Literal, daher wird der eingefügte Wert von der JS-Engine als gewöhnlicher Ausdruck
geparst. Verwenden Sie einen vertrauenswürdigen Serializer, escapen Sie <, wenn das Skript in HTML eingebettet ist, und testen Sie Werte
mit Anführungszeichen, Backslashes, Zeilenumbrüchen, Backticks und ${...}.
Muster 2 - reservedStrings mit einem Template-Literal-Platzhalter
Am besten geeignet für: Template-Engines im Stil von Go oder Jinja, die Begrenzer wie {{ .Field }} verlangen, welche in Backticks
eingeschlossen zufällig gültiger JS-Text sind.
Der Platzhalter steht in einem Template-Literal mit nur einem Quasi-Teil und ohne Interpolation. Unter VM-Obfuskierung wird er über das Array der reservierten Ausdrücke geleitet, sodass die rohe Backtick-Form in der Ausgabe erhalten bleibt.
Quellcode
Obfuscator-Optionen
UI
Um den Platzhalter {{.Config.FeatureFlags}} aus dem obigen Quellcode zu reservieren, tragen Sie diesen regulären Ausdruck in das Feld
Reserved Strings ein, und zwar mit einfachen Backslashes. Die UI speichert den Wert unverändert, daher werden die Backslashes anders als
in JS-Code nicht verdoppelt:

API
Nach der Obfuskierung
Der Platzhalter bleibt Byte für Byte erhalten, einschließlich der umgebenden Backticks.
Ersetzung auf Host-Seite
Ersetzen Sie {{.Config.FeatureFlags}} durch einen JSON-String, der für ein Template-Literal escapt ist. Innerhalb von Backticks muss "
nicht escapt werden, aber ein Backslash, ein Backtick oder ${ in den Nutzdaten würde das Literal dennoch verändern oder beenden. Escapen
Sie daher diese drei:
Zur Laufzeit: JSON.parse(`{"newCheckout":true,"darkMode":false}`) - funktioniert.
Warum sind Template-Literale für String-Werte besser als einfache oder doppelte Anführungszeichen? In Backticks muss " in den
JSON-Nutzdaten nicht escapt werden. Das ist wichtig, weil die meisten serverseitig gerenderten Werte JSON sind und JSON voller doppelter
Anführungszeichen steckt. Bei einem Platzhalter in doppelten Anführungszeichen müssten Sie jedes innere " escapen (siehe Muster 3); mit
Backticks müssen in den Nutzdaten nur die seltenen Backslashes, Backticks und ${ escapt werden.
Muster 3 - reservedStrings mit einem String-Platzhalter in Anführungszeichen
Am besten geeignet für: Quellcode, der ES5 bleiben muss (keine Template-Literale), oder Fälle, in denen die umgebende API ein gewöhnliches String-Literal erwartet.
Der Platzhalter ist ein String-Literal in einfachen oder doppelten Anführungszeichen. Unter VM-Obfuskierung wird er über das Array der
reservierten Strings geleitet, das als mit JSON.stringify serialisiertes JS-Array ausgegeben wird, also unabhängig von den
Anführungszeichen der Eingabe immer in doppelten Anführungszeichen.
Quellcode
Obfuscator-Optionen
UI
Um den Platzhalter {{.Page.Tags}} aus dem obigen Quellcode zu reservieren, tragen Sie diesen regulären Ausdruck in das Feld Reserved Strings
ein, und zwar mit einfachen Backslashes. Die UI speichert den Wert unverändert, daher werden die Backslashes anders als in JS-Code nicht verdoppelt:

API
Nach der Obfuskierung
Ersetzung auf Host-Seite
Da der Platzhalter in einem JS-String mit doppelten Anführungszeichen steht, müssen in den eingefügten JSON-Nutzdaten Backslashes und
innere "-Zeichen escapt werden:
Resultierende Ausgabe:
Zur Laufzeit: JSON.parse("[\"news\",\"tech\",\"release\"]") → ["news","tech","release"].
Vergessen Sie das Escaping, sieht der Browser unausgeglichene Anführungszeichen und wirft einen SyntaxError. Muster 2 umgeht die
doppelten Anführungszeichen vollständig, indem es Backticks verwendet.
Mehrere Platzhalter in einem Programm
Alle drei Muster lassen sich kombinieren. Ein einziger regulärer Ausdruck in reservedStrings mit einer Alternation kann jede
Platzhalterform abdecken, die Ihre Template-Engine erzeugt - hier sowohl den Stil {{ .Field }} als auch %{ .Field }:
Sie können Bezeichner-Platzhalter (für rohe JS-Werte) und String-Platzhalter (für gerendertes JSON) im selben Quellcode mischen. Entscheiden Sie für jeden Platzhalter danach, was der Server tatsächlich einsetzen wird.
Hinweise zur Kompatibilität
vmSelfDefending verhindert eine Ersetzung nach der Obfuskierung
Die Option VM Self Defending erkennt jede Änderung an der obfuskierten Ausgabe nach dem Build, auch eine legitime Template-Ersetzung, und der geschützte Code verweigert dann die Ausführung.
Wenn Sie auf Template-Substitution auf Host-Seite angewiesen sind, setzen Sie vmSelfDefending: false. Lassen Sie auch selfDefending
ausgeschaltet: Unter VM-Obfuskierung hat es keine Wirkung, und ohne VM verbietet es ebenfalls jede Änderung an der Ausgabe.
Tipps zu Platzhaltern
- Verwenden Sie Platzhalter nicht gleichzeitig für Bezeichner und Strings. Ein reservierter Name wie
__TOKEN__und ein reservierter String, der auf__TOKEN__passt, beschreiben zwei verschiedene Codepfade (Array der reservierten Ausdrücke bzw. Array der reservierten Strings). Verwenden Sie für beide unterschiedliche Textformen, zum Beispiel eineUPPER_SNAKE-Konvention für Bezeichner-Platzhalter und eine von Begrenzern umschlossene Form ({{ ... }},%{...},<<<...>>>) für String-Platzhalter. So kann ein Fehler in einem regulären Ausdruck nicht stillschweigend auf den anderen passen. - Reguläre Ausdrücke in
reservedStringswerden auf die rohen String-Werte angewendet. Geprüft wird der Laufzeitwert des Strings, nicht der Quelltext.\{\{[^}]+\}\}passt auf Strings, die{{.something}}(oder eine andere{{...}}-Form) enthalten. Ist Ihr Platzhalter womöglich von weiterem Inhalt umgeben ("prefix-{{.Field}}-suffix"), passt der reguläre Ausdruck trotzdem, aber der gesamte String bleibt erhalten. Planen Sie Ihre Ersetzung entsprechend. - Funktioniert mit jeder Template-Engine. Auch wenn die Beispiele Go-Syntax verwenden, ist an der Integration von javascript-obfuscator nichts Go-spezifisch. Alles, was eine Ersetzung auf String-Ebene an der Ausgabe des Obfuscators vornehmen kann, funktioniert: Rails ERB, Django, PHP-Short-Tags, sed in einer CI-Pipeline usw. Wählen Sie Begrenzer, die Ihre Engine natürlicherweise erzeugt und die nicht mit echter JS-Syntax kollidieren.
