Documentação
/
Receitas
/

Chave do Bytecode Array Encoding

Externalizando a chave do Bytecode Array Encoding

Pro

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

Assistir

Obfuscator.io Async Executor: Async Bytecode Key Getter and Async-Only VM Virtualization

Assistir no YouTube

Obfuscator.io Custom Bytecode Key: Compile-Time Key and Runtime Key Getter (Cookie, Fetch, Decoy Key)

Assistir no YouTube

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, então você nunca precisa lidar com ela. Isso é conveniente, mas o material da chave continua dentro do bundle.

Duas opções permitem tirar a chave do bundle e controlá-la 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 é embutida na saída ofuscada.
  • vmBytecodeArrayEncodingKeyGetter - uma expressão JavaScript que retorna essa mesma chave em tempo de execução. Ela é embutida literalmente e avaliada no navegador quando o código ofuscado carrega.

A ideia é 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 o código rodar, então não é realmente secreta - mas você decide de onde ela vem e quem pode vê-la.

Essas duas opções formam um par, e o ofuscador exige isso: definir uma sem a outra é um erro de validação no momento do build (vmBytecodeArrayEncodingKey sozinha não tem como obter a chave em tempo de execução; um getter sozinho não tem uma chave de tempo de compilação com a qual coincidir). Elas também não fazem nada a menos que o array de bytecode esteja de fato sendo criptografado, então vmBytecodeArrayEncoding: true também precisa estar ativado - ou vmSelfDefending: true, que força vmBytecodeArrayEncoding internamente. Defina as duas chaves junto com uma dessas opções.

Como as duas chaves se combinam

A sua chave nunca é usada sozinha - nos dois lados, ela é misturada com uma chave interna controlada pelo ofuscador:

  • Em 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.
  • Em tempo de execução. O valor para o qual o seu vmBytecodeArrayEncodingKeyGetter resolve é combinado com a mesma chave interna, reconstruída no cliente a partir de vários fatores do ambiente de execução, para decodificar o bytecode.

Como os dois lados misturam a sua chave com a chave interna, o getter precisa resolver para exatamente a mesma string que você passou como vmBytecodeArrayEncodingKey. Nenhuma das partes basta 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 controlar quem recebe a 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 precisa retornar a chave imediatamente quando o código ofuscado carrega. Leia-a de qualquer fonte que já esteja presente no cliente - um cookie, localStorage, uma variável global ou um elemento DOM injetado pelo servidor.

JavaScript

A chave precisa existir antes de o código ofuscado rodar:

JavaScript

Outras fontes síncronas funcionam da mesma forma - escolha a que o seu app já preenche:

JavaScript

Mantenha a chave fora do arquivo ou script que contém o código ofuscado. Colocá-la inline ali anula todo o propósito - uma varredura estática do bundle recuperaria tanto o código quanto a chave. Guarde-a em uma fonte separada e injete a vmBytecodeArrayEncodingKey de tempo de compilação a partir de uma variável de ambiente ou de um segredo, em vez de fazer commit dela.

Obtendo a chave do seu backend (assíncrono)

Requer vmAsyncExecutor · v7.3.0+

Um getter síncrono só consegue ler o que já está no cliente. Para obter a chave do seu servidor - de modo que você possa protegê-la com 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 rodar.

Ativar vmAsyncExecutor também restringe o que é virtualizado: nesse modo, apenas as funções async mais externas são compiladas para a VM, e o código síncrono não é virtualizado (o restante da ofuscação continua sendo aplicado). Se o seu programa for majoritariamente síncrono, envolva o código que você quer proteger em uma função async para que ele continue coberto - veja vmAsyncExecutor para a regra completa.

JavaScript

Um getter que retorna uma Promise requer vmAsyncExecutor. Isso não pode ser verificado no momento do build, então um getter de Promise com vmAsyncExecutor desativado falha em tempo de execução.

No servidor, decida qual chave retornar com base no que a sua aplicação considera confiável - uma sessão validada, uma verificação de licença e assim por diante. Origin ou Referer sozinhos não autenticam quem faz a chamada, e requisições GET da mesma origem podem omitir Origin. O pulo do gato: em vez de rejeitar chamadores não confiáveis, retorne uma chave errada (VM_DECOY_KEY abaixo). O código protegido então falha por conta própria (erros, resultados errados ou uma página que para de responder), o que é mais discreto que um 401 óbvio, que diz ao atacante exatamente o que contornar.

Desative o cache das respostas, para que a chave de um chamador nunca seja servida a outro. Vincule as chaves à versão correta do build e implante chaves e bundles juntos. Um cliente que recebe a chave real ainda pode inspecioná-la em tempo de execução.

JavaScript

Sirva neste endpoint exatamente a mesma string que você passou como vmBytecodeArrayEncodingKey no momento do build. O getter acima busca uma URL relativa (/api/vm-key), então uma cópia do bundle hospedada em outra origem requisita /api/vm-key dessa origem e, portanto, nunca recebe a sua chave; a chave falsa é o que recebe quem consegue alcançar o seu endpoint, mas sem uma sessão confiável (uma requisição não autenticada, um bundle roubado passando por proxy na sua origem). De qualquer forma, a chave real nunca chega e o código protegido não roda.

O que conta como "válido" depende inteiramente da aplicação: uma sessão autenticada, uma licença assinada ou qualquer combinação. Seja o que for que produza a chave, envolva-a em uma Promise, e o executor assíncrono vai aguardá-la antes de rodar a VM.

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 forem diferentes - ou se o getter retornar undefined, null ou uma string vazia -, o código falha em tempo de execução: produz resultados errados, lança um erro de execução comum ou para de responder.

Não há, de propósito, nenhuma mensagem de erro específica para a chave: uma chave que falhou é indistinguível de qualquer outra falha em tempo de execução. Então, quando um bundle protegido por VM só lança erros, retorna resultados errados ou trava depois que esta opção entra em jogo, verifique primeiro o caminho da chave - se o getter resolve na página, se retorna uma string não vazia e se retorna o mesmo valor usado no build.