Externalizando a chave de codificação do array de bytecode
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.
