Documentação
/
Receitas
/

Obtendo a chave do array de bytecode no backend

Obtendo a chave do array de bytecode no backend

Pro
v7.3.0+

Mantenha a chave de descriptografia do bytecode da VM fora do cliente: obtenha-a no seu backend no momento do carregamento com um getter de chave assíncrono, protegido por autenticação, para que um bundle roubado fique inerte.

O problema

O vmBytecodeArrayEncoding criptografa o bytecode da VM com uma chave. Se essa chave for enviada dentro do bundle, qualquer pessoa com o arquivo tem tudo o que precisa para descriptografá-lo. Um vmBytecodeArrayEncodingKeyGetter permite que a chave fique em outro lugar e seja produzida em tempo de execução — mas um getter síncrono só consegue ler o que já está presente no cliente (um global, um cookie, o localStorage). 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.

A solução

Ative o vmAsyncExecutor (v7.3.0+). Com o executor assíncrono, o getter de chave pode retornar uma Promise, então ele pode usar fetch para obter a chave do seu backend antes de a VM rodar. Três opções funcionam em conjunto:

  • vmBytecodeArrayEncoding: true — criptografa o array de bytecode.
  • vmBytecodeArrayEncodingKey — a chave usada em tempo de compilação (mantida no seu servidor, não no bundle).
  • vmBytecodeArrayEncodingKeyGetter — uma expressão JS que retorna essa mesma chave em tempo de execução. Com o vmAsyncExecutor ativado, ela pode retornar uma Promise; sem ele, o getter precisa retornar a chave de forma síncrona.

Configuração de ofuscação do 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())'
});

A expressão do getter é embutida literalmente e avaliada no navegador. Mantenha a chave de tempo de compilação vmBytecodeArrayEncodingKey fora do repositório do cliente — injete-a a partir de uma variável de ambiente ou de um secret no momento do build e sirva exatamente a mesma string a partir do endpoint abaixo.

Como as duas chaves funcionam

Sua chave nunca é usada sozinha — nos dois lados ela é combinada com uma chave interna controlada pelo ofuscador:

  • Tempo de compilação. O vmBytecodeArrayEncodingKey é combinado com uma chave interna que o ofuscador deriva, e o array de bytecode é codificado com a chave combinada resultante.
  • Tempo de execução. O valor para o qual o seu vmBytecodeArrayEncodingKeyGetter resolve — retornado pelo seu servidor — é 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 os dois lados combinam a sua chave com a chave interna, o getter precisa resolver para a exata mesma string que você passou em vmBytecodeArrayEncodingKey. Nenhuma das partes é suficiente sozinha: a 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 servir a sua chave apenas para quem está autenticado mantém um bundle roubado inerte.

Do lado do servidor

O endpoint decide qual chave retornar com base no que a sua aplicação considera confiável — 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 quem não é confiável, retorne uma chave errada. O bytecode então decodifica para lixo e o código protegido falha sozinho, o que é mais discreto do que um 401 óbvio que diz ao atacante exatamente o que ele precisa 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
    );
});