Documentação
/
Receitas
/

Chave de codificação do array de bytecode

Externalizando a chave de codificação do array de bytecode

Pro

Forneça sua própria chave de criptografia de bytecode da VM com vmBytecodeArrayEncodingKey e devolva-a em tempo de execução por meio de um key getter — mantida fora do bundle, lida do armazenamento do cliente ou obtida do seu backend.

O que essas opções fazem

vmBytecodeArrayEncoding criptografa o array de bytecode da VM para que ele não fique na saída como texto puro. Por padrão, a chave de criptografia é derivada do ambiente e reconstruída no cliente, de modo que você nunca precisa lidar com ela. Isso é conveniente, mas o material da chave ainda reside no bundle.

Duas opções permitem que você retire a chave do bundle e a controle você mesmo:

  • vmBytecodeArrayEncodingKey — a chave que você fornece em tempo de compilação. Quando definida, ela é usada no lugar da chave padrão derivada do ambiente e não é incorporada na saída ofuscada.
  • vmBytecodeArrayEncodingKeyGetter — uma expressão JavaScript que retorna essa mesma chave em tempo de execução. Ela é incorporada literalmente e avaliada no navegador quando o código ofuscado é carregado.

O objetivo é a separação: como a chave não está no código, uma varredura puramente estática do bundle não consegue recuperá-la. Ela ainda precisa estar presente em tempo de execução para que o código funcione, portanto não é verdadeiramente secreta — mas você decide de onde ela vem e quem pode vê-la.

Como as duas chaves se combinam

Sua chave nunca é usada sozinha — em ambos os lados ela é combinada com uma chave interna controlada pelo ofuscador:

  • Tempo de compilação. vmBytecodeArrayEncodingKey é combinada com uma chave interna que o ofuscador deriva, e o array de bytecode é codificado com a chave mista resultante.
  • Tempo de execução. O valor para o qual seu vmBytecodeArrayEncodingKeyGetter é resolvido é combinado com a mesma chave interna, reconstruída no cliente a partir de vários fatores de tempo de execução, para decodificar o bytecode.

Como ambos os lados combinam sua chave com a chave interna, o getter deve ser resolvido para exatamente a mesma string que você passou como vmBytecodeArrayEncodingKey. Nenhuma das partes é suficiente sozinha: sua chave sem a chave interna não consegue decodificar o bytecode, e a chave interna é inútil sem a sua — e é por isso que controlar quem recebe sua chave é o que realmente protege o código.

Fornecendo a chave em tempo de execução

Por padrão, o getter é síncrono: a expressão deve retornar a chave imediatamente quando o código ofuscado é carregado. Leia-a de qualquer fonte que já esteja presente no cliente — um cookie, localStorage, uma variável global ou um elemento do DOM injetado pelo servidor.

JavaScriptObfuscator.obfuscate(sourceCode, {
    vmObfuscation: true,
    vmBytecodeArrayEncoding: true,
    vmBytecodeArrayEncodingKey: process.env.VM_KEY,       // e.g. 'mySecretKey123'
    vmBytecodeArrayEncodingKeyGetter: "window.__VM_KEY__" // returns the key at runtime
});

A chave deve existir antes de o código ofuscado ser executado:

// Set by a different script, a server-injected inline script, etc.
window.__VM_KEY__ = 'mySecretKey123';

Outras fontes síncronas funcionam da mesma forma — escolha aquela que seu aplicativo já preenche:

// From a cookie
vmBytecodeArrayEncodingKeyGetter: "document.cookie.match(/vmKey=([^;]+)/)?.[1]"

// From localStorage
vmBytecodeArrayEncodingKeyGetter: "localStorage.getItem('vmKey')"

// From a server-injected meta tag
vmBytecodeArrayEncodingKeyGetter: "document.querySelector('meta[name=\"vm-key\"]').content"

// From a nested object
vmBytecodeArrayEncodingKeyGetter: "window.config.encryption.key"

Obtendo a chave do seu backend (assíncrono)

Requer vmAsyncExecutor · v7.3.0+

Um getter síncrono só pode ler o que já está no cliente. Para obter a chave do seu servidor — de modo que você possa protegê-la por trás de autenticação e revogá-la — o getter precisa ser assíncrono, e isso requer vmAsyncExecutor. Com o executor assíncrono ativado, o getter pode retornar uma Promise, e a VM a aguarda antes de executar.

JavaScriptObfuscator.obfuscate(sourceCode, {
    vmObfuscation: true,
    vmAsyncExecutor: true,
    vmBytecodeArrayEncoding: true,
    vmBytecodeArrayEncodingKey: process.env.VM_KEY,       // kept on your server, not in the bundle
    vmBytecodeArrayEncodingKeyGetter:
        'fetch("/api/vm-key", { credentials: "include" }).then((res) => res.text())'
});

No servidor, decida qual chave retornar com base naquilo em que seu aplicativo confia — uma sessão válida, um Origin ou Referer esperado, uma verificação de licença e assim por diante. O truque: em vez de rejeitar chamadores não confiáveis, retorne uma chave errada. O bytecode então é decodificado em lixo e o código protegido falha por conta própria, o que é mais discreto do que um 401 óbvio que informa a um invasor exatamente o que contornar.

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

Sirva a partir desse endpoint exatamente a mesma string que você passou como vmBytecodeArrayEncodingKey em tempo de build. Uma cópia do bundle sendo executada fora do seu ambiente recebe a chave-isca, descriptografa para nada e fica inerte.

Quando a chave não corresponde

O código ofuscado só funciona quando o getter retorna exatamente a mesma chave usada durante a ofuscação. Se as chaves diferem — ou se o getter retorna undefined, null ou uma string vazia — a descriptografia produz um keystream errado e o código falha em tempo de execução com saída sem sentido ou com um erro comum de tempo de execução.

Deliberadamente, não há uma mensagem de erro distinta e específica da chave: uma chave que falhou é indistinguível de qualquer outra falha em tempo de execução. Portanto, quando um bundle protegido pela VM lança um erro apenas quando essa opção está em uso, verifique primeiro o caminho da chave — que o getter seja resolvido na página, retorne uma string não vazia e retorne o mesmo valor com o qual você fez a build.