Dokumentation
/
Rezepte
/

Den Bytecode-Array-Schlüssel vom Backend abrufen

Den Bytecode-Array-Schlüssel vom Backend abrufen

Pro
v7.3.0+

Halten Sie den Schlüssel zur Entschlüsselung des VM-Bytecodes vom Client fern - ein asynchroner Key-Getter holt ihn beim Laden von Ihrem Backend, abgesichert durch Authentifizierung, sodass ein gestohlenes Bundle wirkungslos bleibt.

Das Problem

vmBytecodeArrayEncoding verschlüsselt den VM-Bytecode mit einem Schlüssel. Wird dieser Schlüssel im Bundle mitgeliefert, besitzt jeder mit der Datei alles, was zur Entschlüsselung nötig ist. Ein vmBytecodeArrayEncodingKeyGetter erlaubt es, den Schlüssel anderswo aufzubewahren und ihn erst zur Laufzeit zu erzeugen – ein synchroner Getter kann jedoch nur lesen, was bereits auf dem Client vorliegt (eine globale Variable, ein Cookie, localStorage). Damit der Schlüssel von Ihrem Server abgerufen werden kann – und Sie ihn hinter einer Authentifizierung absichern und widerrufen können –, muss der Getter asynchron sein.

Die Lösung

Aktivieren Sie vmAsyncExecutor (v7.3.0+). Mit dem asynchronen Executor darf der Key-Getter ein Promise zurückgeben und den Schlüssel somit per fetch von Ihrem Backend holen, bevor die VM startet. Drei Optionen greifen dabei ineinander:

  • vmBytecodeArrayEncoding: true – verschlüsselt das Bytecode-Array.
  • vmBytecodeArrayEncodingKey – der zur Kompilierzeit verwendete Schlüssel (er bleibt auf Ihrem Server und gehört nicht ins Bundle).
  • vmBytecodeArrayEncodingKeyGetter – ein JS-Ausdruck, der genau diesen Schlüssel zur Laufzeit zurückgibt. Mit aktiviertem vmAsyncExecutor darf er ein Promise zurückgeben; ohne diese Option muss der Getter den Schlüssel synchron liefern.

Obfuskierungskonfiguration für den Client

JavaScriptObfuscator.obfuscate(sourceCode, {
    vmObfuscation: true,
    vmAsyncExecutor: true,
    vmBytecodeArrayEncoding: true,
    vmBytecodeArrayEncodingKey: process.env.VM_KEY,            // e.g. 'mySecretKey123'
    vmBytecodeArrayEncodingKeyGetter:
        'fetch("/api/vm-key", { credentials: "include" }).then((res) => res.text())'
});

Der Getter-Ausdruck wird wortwörtlich eingebettet und im Browser ausgewertet. Halten Sie den zur Kompilierzeit verwendeten vmBytecodeArrayEncodingKey aus Ihrem Client-Repository heraus – injizieren Sie ihn beim Build aus einer Umgebungsvariablen oder einem Secret und liefern Sie exakt dieselbe Zeichenkette über den unten beschriebenen Endpunkt aus.

Wie die beiden Schlüssel zusammenspielen

Ihr Schlüssel wird nie allein verwendet – auf beiden Seiten wird er mit einem internen Schlüssel gemischt, den der Obfuscator kontrolliert:

  • Zur Kompilierzeit. vmBytecodeArrayEncodingKey wird mit einem internen Schlüssel kombiniert, den der Obfuscator ableitet, und das Bytecode-Array wird mit dem daraus entstehenden Mischschlüssel codiert.
  • Zur Laufzeit. Der Wert, zu dem Ihr vmBytecodeArrayEncodingKeyGetter aufgelöst wird – also die Antwort Ihres Servers –, wird mit demselben internen Schlüssel kombiniert, der auf dem Client aus verschiedenen Laufzeitfaktoren rekonstruiert wird, um den Bytecode zu decodieren.

Da beide Seiten Ihren Schlüssel mit dem internen Schlüssel mischen, muss der Getter zu exakt derselben Zeichenkette aufgelöst werden, die Sie als vmBytecodeArrayEncodingKey übergeben haben. Kein Teil genügt für sich allein: Ihr Schlüssel ohne den internen Schlüssel kann den Bytecode nicht decodieren, und der interne Schlüssel ist ohne Ihren nutzlos – genau deshalb bleibt ein gestohlenes Bundle wirkungslos, wenn Sie Ihren Schlüssel nur an authentifizierte Aufrufer ausliefern.

Serverseite

Der Endpunkt entscheidet anhand dessen, worauf Ihre Anwendung vertraut, welcher Schlüssel zurückgegeben wird – eine gültige Sitzung, ein erwarteter Origin- oder Referer-Header, eine Lizenzprüfung und so weiter. Der Kniff: Statt nicht vertrauenswürdige Aufrufer abzuweisen, geben Sie einen falschen Schlüssel zurück. Der Bytecode decodiert dann zu Datenmüll, und der geschützte Code scheitert von allein – das ist unauffälliger als ein offensichtlicher 401, der einem Angreifer genau verrät, was er umgehen muss.

// Express example — the exact checks depend on your app
app.get('/api/vm-key', (req, res) => {
    const origin = req.get('origin');
    const trusted =
        req.session?.user &&                       // a valid session, and
        origin === 'https://app.example.com';      // the expected production origin

    res.type('text/plain').send(
        // Real key for valid users; a decoy for everyone else
        // (no session, or a localhost / unexpected origin).
        trusted ? process.env.VM_KEY : process.env.VM_DECOY_KEY
    );
});