Dokumentation
/
Rezepte
/

Template-Substitution auf Host-Seite

Template-Substitution auf Host-Seite

Pro
v6.10.0+

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.

JavaScript

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 erwartetMuster 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:

JavaScript

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 ${...}.

JavaScript

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

JavaScript

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:

Text

Das Feld Reserved Strings in der Obfuscator-UI mit dem regulären Ausdruck, eingegeben mit einfachen Backslashes

API

JavaScript

Nach der Obfuskierung

JavaScript

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:

Code

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

JavaScript

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:

Text

Das Feld Reserved Strings in der Obfuscator-UI mit dem regulären Ausdruck, eingegeben mit einfachen Backslashes

API

JavaScript

Nach der Obfuskierung

JavaScript

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:

Code

Resultierende Ausgabe:

JavaScript

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 }:

JavaScript

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 eine UPPER_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 reservedStrings werden 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.