Sustitución de plantillas en el servidor
Inyecta valores renderizados en el servidor (por ejemplo, marcadores `{{ .Field }}` al estilo de las plantillas de Go) en JavaScript ofuscado con VM sin romper el pipeline de ofuscación.
El problema
Tu backend (Go, Rails, Django, PHP, …) sirve un archivo JavaScript cuyo contenido debe renderizarse parcialmente
en cada solicitud: un endpoint de la API, una lista de feature flags, un bloque de estado inicial, un id de compilación, un nonce. Ofuscas el
JS con vmObfuscation: true, pero también necesitas que el motor de plantillas sustituya los marcadores en la salida
ofuscada después de que termine la ofuscación. reservedNames y reservedStrings mantienen un marcador visible en la
salida para que pueda reemplazarse. Sin ellas, un marcador de cadena se absorbe en el bytecode de la VM y un marcador de
identificador se emite en varios lugares reescritos, así que el motor de plantillas no encuentra nada que sustituir o rompe la salida.
Considera cargar los valores como datos
Si puedes, evita por completo editar la salida protegida: carga la configuración de ejecución como datos antes de que se ejecute
el bundle protegido. Mantenla en un script separado u obtenla de un endpoint autenticado. Así la salida protegida queda intacta y
vmSelfDefending puede seguir activada. En cualquier caso, los datos que se entregan a un navegador son visibles para ese navegador.
Usa los patrones siguientes cuando los valores realmente tengan que sustituirse dentro del archivo ofuscado.
Una nota sobre los ejemplos en Go: hacen un reemplazo simple de cadenas (strings.ReplaceAll / strings.NewReplacer) sobre
la salida ofuscada y gestionan su propio escapado, que se muestra en cada patrón. No pasan por el paquete html/template
de Go, que aplicaría encima su propio escapado contextual de JavaScript y escaparía dos veces el contenido. Si renderizas
con html/template, elimina el escapado manual y deja que el motor escape una sola vez.
¿Qué opción elijo?
| El valor del servidor es… | Usa |
|---|---|
| Un valor JS sin procesar (array, objeto, número, booleano: cualquier valor JSON) | Patrón 1: reservedNames + marcador de identificador |
| Una cadena (el caso habitual: JSON renderizado, un id de compilación, un nonce) | Patrón 2: reservedStrings + marcador de plantilla literal |
| Una cadena, cuando el código debe seguir siendo ES5 o la API que lo rodea necesita un literal de cadena entre comillas | Patrón 3: reservedStrings + marcador de cadena entre comillas |
La sustitución posterior a la ofuscación es incompatible con vmSelfDefending: true. Consulta las
Notas de compatibilidad al final de esta receta.
Patrón 1: reservedNames con un marcador de identificador
Ideal para: inyectar expresiones JS sin procesar (arrays, objetos, números, …) sin preocuparte de escapar comillas.
Elige un identificador distintivo que nunca vaya a coincidir con código real, haz referencia a él directamente y añade una regex que
lo capture en reservedNames. Con la ofuscación VM, el identificador pasa por el array de expresiones reservadas y
aparece literalmente en la salida, por ejemplo:
Reemplaza el identificador por una expresión completa serializada en JSON. No pegues el valor entre comillas ni entre backticks:
el identificador no está dentro de un literal de cadena, así que el motor JS analiza el valor inyectado como una expresión
normal. Usa un serializador de confianza, escapa < cuando el script esté incrustado en HTML y prueba valores que contengan
comillas, barras invertidas, saltos de línea, backticks y ${...}.
Patrón 2: reservedStrings con un marcador de plantilla literal
Ideal para: motores de plantillas al estilo de Go o Jinja que exigen delimitadores como {{ .Field }}, que resultan ser texto JS
válido cuando se envuelven en backticks.
El marcador vive dentro de una plantilla literal de un solo quasi, sin interpolaciones. Con la ofuscación VM pasa por el array de expresiones reservadas, lo que conserva en la salida la forma original con backticks.
Código fuente
Opciones del ofuscador
Interfaz
Para reservar el marcador {{.Config.FeatureFlags}} del código anterior, añade esta regex al campo Reserved Strings,
con barras invertidas simples. La interfaz guarda el valor literalmente, así que, a diferencia del código JS, las barras invertidas no se
duplican:

API
Tras la ofuscación
El marcador se conserva byte a byte, incluidos los backticks que lo rodean.
Sustitución en el servidor
Reemplaza {{.Config.FeatureFlags}} por una cadena JSON escapada para una plantilla literal. Los backticks no exigen escapar las
" internas, pero una barra invertida, un backtick o ${ dentro del contenido seguirían modificando o cerrando el literal, así que escapa
esos tres:
En tiempo de ejecución: JSON.parse(`{"newCheckout":true,"darkMode":false}`), que funciona.
¿Por qué preferir plantillas literales a comillas simples o dobles para valores de cadena? Los backticks no exigen escapar "
dentro del contenido JSON. Esto importa porque la mayoría de los valores renderizados en el servidor son JSON, y el JSON está lleno de comillas
dobles. Con un marcador entre comillas dobles tendrías que escapar cada " interna (consulta el Patrón 3); con backticks
solo hay que escapar las escasas barras invertidas, backticks y ${ del contenido.
Patrón 3: reservedStrings con un marcador de cadena entre comillas
Ideal para: código fuente que debe seguir siendo ES5 (sin plantillas literales), o casos en los que la API que lo rodea espera un literal de cadena normal.
El marcador es un literal de cadena entre comillas simples o dobles. Con la ofuscación VM pasa por el
array de cadenas reservadas, que se emite como un array JS serializado con JSON.stringify: siempre entre comillas dobles, sea cual sea
el estilo de comillas de la entrada.
Código fuente
Opciones del ofuscador
Interfaz
Para reservar el marcador {{.Page.Tags}} del código anterior, añade esta regex al campo Reserved Strings,
con barras invertidas simples. La interfaz guarda el valor literalmente, así que, a diferencia del código JS, las barras invertidas no se duplican:

API
Tras la ofuscación
Sustitución en el servidor
Como el marcador está dentro de una cadena JS entre comillas dobles, el contenido JSON inyectado debe tener escapadas sus barras invertidas y
sus caracteres " internos:
Salida resultante:
En tiempo de ejecución: JSON.parse("[\"news\",\"tech\",\"release\"]") → ["news","tech","release"].
Si olvidas el escapado, el navegador verá comillas desequilibradas y lanzará un SyntaxError. El Patrón 2 evita por completo las
comillas dobles al usar backticks.
Varios marcadores en un mismo programa
Los tres patrones se pueden combinar. Una sola regex de reservedStrings con una alternancia puede capturar todas las formas de marcador que
emita tu motor de plantillas; aquí, los estilos {{ .Field }} y %{ .Field }:
Puedes mezclar marcadores de identificador (para valores JS sin procesar) y marcadores de cadena (para JSON renderizado) en el mismo código fuente: elige para cada marcador según lo que el servidor vaya a inyectar realmente.
Notas de compatibilidad
vmSelfDefending rompe la sustitución posterior a la ofuscación
VM Self Defending detecta cualquier cambio en la salida ofuscada después de generarla, incluida una sustitución legítima de plantillas, y el código protegido se niega entonces a ejecutarse.
Si dependes de la sustitución de plantillas en el servidor, establece vmSelfDefending: false. Deja también selfDefending desactivada:
no tiene ningún efecto con la ofuscación VM, y sin VM también impide cualquier cambio en la salida.
Consejos sobre los marcadores
- No reutilices marcadores entre identificadores y cadenas. Un nombre reservado como
__TOKEN__y una cadena reservada que coincida con__TOKEN__describen dos rutas de código distintas (array de expresiones reservadas frente a array de cadenas reservadas). Usa formas textuales distintas para cada uno: por ejemplo, una convenciónUPPER_SNAKEpara los marcadores de identificador y una forma envuelta en delimitadores ({{ ... }},%{...},<<<...>>>) para los marcadores de cadena. Así, un error en una regex no puede coincidir silenciosamente con el otro. - Las regex de
reservedStringsse aplican a los valores de cadena sin procesar. La regex se compara con el valor de la cadena en tiempo de ejecución, no con el texto fuente.\{\{[^}]+\}\}coincide con cadenas que contienen{{.something}}(o cualquier otra forma{{...}}). Si tu marcador puede ir envuelto en contenido adicional ("prefix-{{.Field}}-suffix"), la regex sigue coincidiendo, pero se conserva la cadena entera: planifica tu sustitución en consecuencia. - Funciona con cualquier motor de plantillas. Aunque los ejemplos usan sintaxis de Go, nada en la integración de javascript-obfuscator es específico de Go. Cualquier herramienta capaz de hacer un reemplazo a nivel de cadena sobre la salida del ofuscador servirá: Rails ERB, Django, las etiquetas cortas de PHP, sed en un pipeline de CI, etc. Elige delimitadores que tu motor emita de forma natural y que no choquen con la sintaxis real de JS.
