Documentación
/
Recetas
/

Obtener la clave del array de bytecode desde el backend

Obtener la clave del array de bytecode desde el backend

Pro
v7.3.0+

Mantén la clave de descifrado del bytecode de la VM fuera del cliente: obtenla de tu backend en el momento de la carga con un key getter asíncrono, protegido tras la autenticación para que un bundle robado quede inservible.

El problema

vmBytecodeArrayEncoding cifra el bytecode de la VM con una clave. Si esa clave se envía dentro del bundle, cualquiera que tenga el archivo dispone de todo lo necesario para descifrarlo. Un vmBytecodeArrayEncodingKeyGetter permite que la clave viva en otro lugar y se produzca en tiempo de ejecución, pero un getter síncrono solo puede leer lo que ya está presente en el cliente (una global, una cookie, localStorage). Para obtener la clave de tu servidor —de modo que puedas protegerla tras la autenticación y revocarla—, el getter tiene que ser asíncrono.

La solución

Activa vmAsyncExecutor (v7.3.0+). Con el ejecutor asíncrono, el key getter puede devolver una Promise, de modo que puede hacer fetch de la clave desde tu backend antes de que se ejecute la VM. Tres opciones trabajan juntas:

  • vmBytecodeArrayEncoding: true — cifra el array de bytecode.
  • vmBytecodeArrayEncodingKey — la clave usada en tiempo de compilación (guardada en tu servidor, no en el bundle).
  • vmBytecodeArrayEncodingKeyGetter — una expresión JS que devuelve esa misma clave en tiempo de ejecución. Con vmAsyncExecutor activada puede devolver una Promise; sin él, el getter debe devolver la clave de forma síncrona.

Configuración de ofuscación del cliente

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())'
});

La expresión del getter se incrusta literalmente y se evalúa en el navegador. Mantén la clave de tiempo de compilación vmBytecodeArrayEncodingKey fuera del repositorio de tu cliente: inyéctala desde una variable de entorno o un secreto en el momento del build, y sirve exactamente la misma cadena desde el endpoint de abajo.

Cómo funcionan las dos claves

Tu clave nunca se usa por sí sola: en ambos lados se mezcla con una clave interna que el ofuscador controla:

  • En tiempo de compilación. vmBytecodeArrayEncodingKey se combina con una clave interna que el ofuscador deriva, y el array de bytecode se codifica con la clave mezclada resultante.
  • En tiempo de ejecución. El valor al que se resuelve tu vmBytecodeArrayEncodingKeyGetter —devuelto por tu servidor— se combina con la misma clave interna, reconstruida en el cliente a partir de varios factores del entorno de ejecución, para decodificar el bytecode.

Como ambos lados mezclan tu clave con la clave interna, el getter debe resolverse a la misma cadena exacta que pasaste como vmBytecodeArrayEncodingKey. Ninguna de las piezas es suficiente por sí sola: tu clave sin la clave interna no puede decodificar el bytecode, y la clave interna es inútil sin la tuya, motivo por el cual servir tu clave solo a quienes están autenticados mantiene un bundle robado inservible.

Lado del servidor

El endpoint decide qué clave devolver en función de lo que tu aplicación considere de confianza: una sesión válida, un Origin o Referer esperado, una comprobación de licencia, etc. El truco: en lugar de rechazar a quienes no son de confianza, devuelve una clave incorrecta. El bytecode se decodifica entonces a basura y el código protegido falla por sí solo, lo cual es más sigiloso que un 401 evidente que le dice al atacante exactamente qué debe eludir.

// 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
    );
});