Obtendo a chave do array de bytecode no backend
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 ovmAsyncExecutorativado, ela pode retornar umaPromise; 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
vmBytecodeArrayEncodingKeyGetterresolve — 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
);
});
