Documentação
/
Receitas
/

Substituição de templates no servidor

Substituição de templates no servidor

Pro
v6.10.0+

Injete valores renderizados pelo servidor (por exemplo, placeholders `{{ .Field }}` no estilo de template do Go) em JavaScript ofuscado por VM sem quebrar o pipeline de ofuscação.

O problema

O seu backend (Go, Rails, Django, PHP, …) serve um arquivo JavaScript cujo conteúdo precisa ser parcialmente renderizado a cada requisição - um endpoint de API, uma lista de feature flags, um blob de estado inicial, um id de build, um nonce. Você ofusca o JS com vmObfuscation: true, mas também precisa que o template engine substitua placeholders na saída ofuscada depois que a ofuscação termina. reservedNames e reservedStrings mantêm um placeholder visível na saída para que ele possa ser substituído. Sem eles, um placeholder de string é absorvido pelo bytecode da VM e um placeholder de identificador é emitido em vários pontos reescritos, então o template engine ou não encontra nada para substituir ou quebra a saída.

Considere carregar os valores como dados

Se puder, evite completamente editar a saída protegida: carregue a configuração de execução como dados antes de o bundle protegido rodar. Mantenha-a em um script separado ou obtenha-a de um endpoint autenticado. Isso preserva a saída protegida, de modo que vmSelfDefending pode continuar ativado. Dados entregues a um navegador ficam visíveis para esse navegador de qualquer forma.

JavaScript

Use os padrões abaixo quando os valores realmente precisarem ser substituídos dentro do arquivo ofuscado.

Uma observação sobre os exemplos em Go: eles fazem uma substituição simples de string (strings.ReplaceAll / strings.NewReplacer) na saída ofuscada e cuidam do próprio escape, mostrado em cada padrão. Eles não passam pelo pacote html/template do Go - isso aplicaria por cima o escape contextual de JavaScript do próprio pacote, escapando o payload duas vezes. Se você renderizar pelo html/template, remova o escape manual e deixe o engine escapar uma única vez.

Qual opção escolher?

O valor do servidor é…Use
Um valor JS bruto (array, objeto, número, booleano - qualquer valor JSON)Padrão 1 - reservedNames + placeholder de identificador
Uma string (o caso comum - JSON renderizado, um id de build, um nonce)Padrão 2 - reservedStrings + placeholder em template literal
Uma string, quando o código precisa continuar ES5 ou a API ao redor exige um literal de string entre aspasPadrão 3 - reservedStrings + placeholder em string entre aspas

A substituição após a ofuscação é incompatível com vmSelfDefending: true. Veja as Notas de compatibilidade no fim desta receita.

Padrão 1 - reservedNames com um placeholder de identificador

Ideal para: injetar expressões JS brutas (arrays, objetos, números, …) sem nenhuma preocupação com escape de aspas.

Escolha um identificador característico que nunca vá colidir com o código real, referencie-o diretamente e liste em reservedNames uma regex que corresponda a ele. Na ofuscação VM, o identificador passa pelo array de expressões reservadas e aparece literalmente na saída, por exemplo:

JavaScript

Substitua o identificador por uma expressão completa serializada em JSON. Não cole o valor dentro de aspas ou crases - o identificador não está dentro de um literal de string, então o valor injetado é interpretado pelo motor JS como uma expressão comum. Use um serializador confiável, escape < quando o script estiver embutido em HTML e teste valores que contenham aspas, barras invertidas, quebras de linha, crases e ${...}.

JavaScript

Padrão 2 - reservedStrings com um placeholder em template literal

Ideal para: template engines no estilo Go / Jinja que exigem delimitadores como {{ .Field }}, que por acaso são texto JS válido quando envolvidos em crases.

O placeholder fica dentro de um template literal com um único quasi e sem interpolação. Na ofuscação VM, ele passa pelo array de expressões reservadas, o que preserva a forma original com crases na saída.

Código-fonte

JavaScript

Opções do ofuscador

Interface

Para reservar o placeholder {{.Config.FeatureFlags}} do código-fonte acima, adicione esta regex ao campo Reserved Strings - com barras invertidas simples. A interface armazena o valor literalmente, então, ao contrário do código JS, as barras invertidas não são duplicadas:

Texto

Campo Reserved Strings na interface do ofuscador com a regex digitada usando barras invertidas simples

API

JavaScript

Após a ofuscação

JavaScript

O placeholder é preservado byte a byte, incluindo as crases ao redor.

Substituição no servidor

Substitua {{.Config.FeatureFlags}} por uma string JSON escapada para um template literal. Crases não exigem escapar as " internas, mas uma barra invertida, uma crase ou ${ dentro do payload ainda mudariam ou encerrariam o literal, então escape esses três:

Código

Em tempo de execução: JSON.parse(`{"newCheckout":true,"darkMode":false}`) - funciona.

Por que preferir template literals a aspas simples/duplas para valores de string? Crases não exigem escapar " dentro do payload JSON. Isso importa porque a maioria dos valores renderizados pelo servidor é JSON, e JSON é cheio de aspas duplas. Com um placeholder entre aspas duplas, você precisaria escapar cada " interna (veja o Padrão 3); com crases, o payload só precisa ter escapadas as raras barras invertidas, crases e ${.

Padrão 3 - reservedStrings com um placeholder em string entre aspas

Ideal para: código-fonte que precisa continuar em ES5 (sem template literals) ou casos em que a API ao redor espera um literal de string comum.

O placeholder é um literal de string entre aspas simples ou duplas. Na ofuscação VM, ele passa pelo array de strings reservadas, que é emitido como um array JS serializado com JSON.stringify - sempre com aspas duplas, independentemente do estilo de aspas da entrada.

Código-fonte

JavaScript

Opções do ofuscador

Interface

Para reservar o placeholder {{.Page.Tags}} do código-fonte acima, adicione esta regex ao campo Reserved Strings - com barras invertidas simples. A interface armazena o valor literalmente, então, ao contrário do código JS, as barras invertidas não são duplicadas:

Texto

Campo Reserved Strings na interface do ofuscador com a regex digitada usando barras invertidas simples

API

JavaScript

Após a ofuscação

JavaScript

Substituição no servidor

Como o placeholder fica dentro de uma string JS entre aspas duplas, o payload JSON injetado precisa ter as barras invertidas e os caracteres " internos escapados:

Código

Saída resultante:

JavaScript

Em tempo de execução: JSON.parse("[\"news\",\"tech\",\"release\"]") → ["news","tech","release"].

Se você esquecer o escape, o navegador vai ver aspas desbalanceadas e lançar um SyntaxError. O Padrão 2 evita totalmente as aspas duplas usando crases.

Vários placeholders em um mesmo programa

Os três padrões podem ser combinados. Uma única regex de reservedStrings com uma alternância pode corresponder a todos os formatos de placeholder que o seu template engine emite - aqui, tanto no estilo {{ .Field }} quanto no estilo %{ .Field }:

JavaScript

Você pode misturar placeholders de identificador (para valores JS brutos) e placeholders de string (para JSON renderizado) no mesmo código-fonte - escolha para cada placeholder com base no que o servidor vai realmente injetar.

Notas de compatibilidade

vmSelfDefending quebra a substituição após a ofuscação

O VM Self Defending detecta qualquer alteração na saída ofuscada depois do build, inclusive uma substituição legítima de template, e o código protegido então se recusa a rodar.

Se você depende da substituição de templates no servidor, defina vmSelfDefending: false. Deixe selfDefending desativado também: ele não tem efeito na ofuscação VM e, sem a VM, também proíbe qualquer alteração na saída.

Dicas sobre placeholders

  • Não reutilize placeholders entre identificadores e strings. Um nome reservado como __TOKEN__ e uma string reservada que corresponde a __TOKEN__ descrevem dois caminhos de código diferentes (array de expressões reservadas vs. array de strings reservadas). Use formatos textuais distintos para cada um - por exemplo, uma convenção UPPER_SNAKE para placeholders de identificador e um formato envolvido por delimitadores ({{ ... }}, %{...}, <<<...>>>) para placeholders de string. Assim, um bug em uma regex não pode corresponder silenciosamente à outra.
  • As regexes de reservedStrings rodam sobre os valores brutos das strings. A regex é comparada com o valor da string em tempo de execução, não com o texto do código-fonte. \{\{[^}]+\}\} corresponde a strings que contêm {{.something}} (ou qualquer outro formato {{...}}). Se o seu placeholder puder estar envolvido em conteúdo extra ("prefix-{{.Field}}-suffix"), a regex ainda corresponde, mas a string inteira é preservada - planeje a substituição de acordo.
  • Funciona com qualquer template engine. Embora os exemplos usem a sintaxe do Go, nada na integração com o javascript-obfuscator é específico do Go. Qualquer coisa capaz de fazer uma substituição em nível de string na saída do ofuscador funciona: Rails ERB, Django, short tags do PHP, sed em um pipeline de CI etc. Escolha delimitadores que o seu engine emita naturalmente e que não colidam com a sintaxe JS real.