# Cómo funciona el ofuscador de JavaScript AfterPack

Qué hace la salida estándar (Free) con tu código, cómo resiste la reversión y qué puede recuperar realmente un atacante.

Source: https://www.afterpack.dev/es/docs/concepts

AfterPack es un protector de JavaScript que actúa después del build. Toma el bundle que produce tu cadena de herramientas y lo reescribe en algo que una persona, o un agente de IA, no puede leer, buscar ni modificar sin un esfuerzo desproporcionado. Se integra en [tu build actual](https://www.afterpack.dev/es/docs/frameworks) con una línea, o se ejecuta [sobre el directorio de salida](https://www.afterpack.dev/es/docs/cli) sin ningún plugin.

## Por qué fallan las capas de ofuscación fijas

La mayoría de los ofuscadores entregan dos cosas que un atacante puede explotar: una pila de capas aplicadas una tras otra y estructuras prefabricadas que una herramienta reconoce a simple vista (un array de cadenas, aunque esté cifrado, un intérprete de VM propio, un arranque de decodificador reconocible). La receta para revertirlo es mecánica: identificar una capa o estructura por su firma, quitarla y repetir hasta que aparezca el original. `webcrack` y `restringer` hacen exactamente eso, en microsegundos.

AfterPack no entrega una pila de capas ni un decodificador prefabricado. Cada build tiene una forma distinta, así que las herramientas escritas contra los patrones de un build fallan en el siguiente. Quien ejecuta tu código puede seguir leyendo los valores que usa. [Cómo se compara](https://www.afterpack.dev/es/docs/comparison) con esas herramientas tiene su propia página.

*(animated fixed-pipeline vs per-build-shape comparison — see the web page)*

## Los agentes de IA cambiaron la amenaza

Un modelo de última generación no se limita a resumir código minificado: agentes como Claude Code ya desofuscan la salida de los ofuscadores tradicionales. En un experimento público, un agente devolvió tanto `javascript-obfuscator` como una herramienta comercial (JScrambler) a una lógica legible en unos diez minutos ([el experimento está descrito en nuestro blog](https://www.afterpack.dev/es/blog/ai-deobfuscates-javascript)).

El renombrado, un array de cadenas y unas cuantas capas ya no aportan protección: son exactamente los patrones que mejor manejan estas herramientas y modelos. El [modelo de amenaza](https://www.afterpack.dev/es/docs/threat-model) ordena qué adversario derrota cada técnica. Lo que sigue funcionando contra las herramientas de patrones es una forma nueva en cada build: un reconocedor escrito para un build hay que reescribirlo para el siguiente.

## Qué hace ya la ofuscación estándar

AfterPack se ejecuta después de tu bundler, sobre la salida compilada, y nunca toca tu código fuente, mediante un [plugin de framework](https://www.afterpack.dev/es/docs/frameworks) o la [CLI posterior al build](https://www.afterpack.dev/es/docs/cli#what-a-run-does). [Inicio rápido](https://www.afterpack.dev/es/docs/quickstart) es la versión de cinco minutos.

*(build flow diagram — see the web page)*

Free aplica todo esto:

- **Codifica tus literales de cadena.** Con cualquier [objetivo de complejidad](https://www.afterpack.dev/es/docs/config#complexity) positivo, el [mínimo de cadenas](https://www.afterpack.dev/es/docs/config#strings-encode) está activado, así que un literal de cadena se reescribe en un decodificador en tiempo de ejecución elegido por la [semilla](https://www.afterpack.dev/es/docs/config#seed) en lugar de quedar en el artefacto como texto. Está activado por defecto y es la mayor diferencia respecto al código minificado. Los prólogos de directivas, los operandos de `typeof`, los nombres de propiedad que necesita el runtime y los nombres de exportación siguen siendo legibles, y algunos valores de cadena del código fuente pueden quedar dentro de bundles de terceros muy minificados; el build informa de cuántos sobrevivieron y el [Protection Map](https://www.afterpack.dev/es/docs/protection-map) los enumera.
- **Reescribe tu lógica en formas [equivalentes](https://www.afterpack.dev/es/docs/semantic-contract) que las herramientas estándar no pueden deshacer**: aritmética booleana mixta, predicados opacos, una VM de bytecode para expresiones enteras. Un lector estático aún puede llegar de vuelta a tu original con un esfuerzo real, y una herramienta escrita contra las formas de un build se rompe en el siguiente. [`transforms.<kind>.enabled`](https://www.afterpack.dev/es/docs/config#transforms-kind-enabled) activa o desactiva cada familia.
- **Remodela el control y la estructura**: aplanamiento del flujo de control, ramas muertas, anidamiento de ámbitos, entrelazado de dependencias cruzadas.
- **Lo cambia todo en cada build**, mediante el [polimorfismo](#a-new-program-every-build) por build (abajo).

El valor por defecto sin configuración es [`light`](https://www.afterpack.dev/es/docs/config#preset): el [mínimo de cadenas](https://www.afterpack.dev/es/docs/config#strings-encode) y los identificadores renombrados, sin [inflación](https://www.afterpack.dev/es/docs/config#inflation-max) estructural. Subir el [preset](https://www.afterpack.dev/es/docs/presets) a `medium` o superior añade el trabajo estructural descrito arriba; la legibilidad de las cadenas ya está en su mínimo sea cual sea el preset. Todo esto se ejecuta en todos los niveles, Free incluido; [Niveles](https://www.afterpack.dev/es/docs/tiers) es donde se define la frontera entre Free y Pro.

## Un programa nuevo en cada build

AfterPack deriva su salida de una [semilla](https://www.afterpack.dev/es/docs/config#seed) por build que es aleatoria por defecto. Dos builds del mismo código fuente salen estructuralmente distintos: nombres distintos, codificaciones distintas, una forma distinta en todas partes.

A partir de `medium`, esto va más allá de una reorganización cosmética. AfterPack cambia la estructura del propio programa, no solo su texto: el flujo de control se aplana y se reordena, los valores se recalculan en cada uso en lugar de almacenarse, y partes del bundle que eran independientes salen entrelazadas entre sí. Lo que se publica es un equivalente funcional de lo que escribiste, un programa distinto que [se comporta igual](https://www.afterpack.dev/es/docs/semantic-contract), y la semilla decide qué equivalente obtienes esta vez.

Así que las herramientas escritas contra los patrones de un build fallan en el siguiente, y un parche hay que volver a encontrarlo en cada release. Quien ejecuta el código puede seguir leyendo los valores que usa. Publicar a menudo repite ese coste cada vez. [Builds y releases](https://www.afterpack.dev/es/docs/builds) explica cómo convertirlo en un hábito.

Para una salida reproducible, por aciertos de caché o auditorías, fija la semilla con [`--seed=git`](https://www.afterpack.dev/es/docs/config#seed) en la [CLI](https://www.afterpack.dev/es/docs/cli#flags), o con [`seed: "git"`](https://www.afterpack.dev/es/docs/config#seed) en un plugin. Ambos la derivan de tu commit. Consulta [Builds y versiones](https://www.afterpack.dev/es/docs/builds).

## Qué puede recuperar realmente un atacante

No todas las transformaciones elevan el coste de la reversión de la misma forma. AfterPack clasifica su protección en [clases de reversión](https://www.afterpack.dev/es/docs/reversal-classes); saber cuál cubre una región te dice con qué se queda un atacante.

Son clases de atacante estático: describen a qué se enfrenta alguien que lee los bytes, con grep, herramientas de AST o un LLM que explica código sin ejecutarlo. Un atacante que ejecuta el artefacto bajo un runtime instrumentado es un caso aparte y más fuerte, tratado en [Alcance](#scope) más abajo y en la página del [modelo de amenaza](https://www.afterpack.dev/es/docs/threat-model).

Un build con cualquier preset se queda en la clase renombrado y codificado en todo el bundle; un preset más pesado hace más de ese trabajo, no pasa a otra clase. El [Protection Map](https://www.afterpack.dev/es/docs/protection-map) colorea cada token según su complejidad y nombra la clase del código que selecciones.

Las [sobrescrituras por región](https://www.afterpack.dev/es/docs/directives#what-you-can-set), [`preset=hard`](https://www.afterpack.dev/es/docs/config#preset) y [`preset=extreme`](https://www.afterpack.dev/es/docs/config#preset), suben el [objetivo de complejidad](https://www.afterpack.dev/es/docs/config#complexity) de una región y fuerzan su [mínimo de cadenas](https://www.afterpack.dev/es/docs/config#strings-encode), concentrando ese mismo trabajo donde tú lo pides.

## Cómo sobreviven los secretos

Proteger un secreto usa un mecanismo distinto al de remodelar tu código. Un secreto (una clave de API, una semilla de licencia, un umbral) aparece en cualquier programa que se comporte igual, así que remodelar el código a su alrededor no lo oculta.

Lo que oculta un secreto de un lector estático o de un LLM a ciegas es la **no materialización**: el valor nunca aparece en la salida. [`transforms.comparisonHardening.enabled`](https://www.afterpack.dev/es/docs/config#transforms-kind-enabled) la consigue para un literal con el que tu código compara, reescribiendo la comparación como una comprobación de digest unidireccional, de modo que solo se publica el digest. [`preset=extreme`](https://www.afterpack.dev/es/docs/config#preset) sobre un literal secreto hace que recuperarlo sea caro, y el literal sigue existiendo en la salida.

Un atacante que ejecuta tu código bajo un runtime instrumentado puede llegar a cualquier secreto que use el propio código. Ningún ofuscador en tiempo de build cambia eso: si el programa en ejecución necesita el valor, un atacante dinámico decidido puede observarlo. La no materialización vence al lector estático y al LLM a ciegas, y eleva el coste de volver a extraerlo en cada rotación. No hace irrecuperable un secreto del lado del cliente. Los secretos de alto valor deben estar en el servidor, donde el cliente nunca los tiene.

## Alcance

Una comprobación de licencia en una región [`/* @afterpack preset=hard */`](https://www.afterpack.dev/es/docs/config#preset), publicada a menudo con una [semilla](https://www.afterpack.dev/es/docs/config#seed) variable, no compensa las horas de ingeniería necesarias para romperla antes de que llegue la siguiente versión. Para la mayoría de los productos, ese es el listón adecuado.

La defensa es económica. Con tiempo ilimitado, cualquier código del lado del cliente puede revertirse en cierta medida. El objetivo es llevar el coste por encima del valor y hacer que ese coste se repita en cada versión. Cuando un activo vale más que cualquier esfuerzo de reversión, mantenlo en el servidor, donde el cliente nunca lo tiene. AfterPack refuerza esa arquitectura; [no la sustituye](https://www.afterpack.dev/es/docs/threat-model#what-afterpack-does-not-replace). La [guía de buenas prácticas](https://www.afterpack.dev/es/docs/best-practices) explica cómo invertir la protección donde cuenta.

## Siguiente

- [Clases de reversión](https://www.afterpack.dev/es/docs/reversal-classes): la tabla de tres clases en profundidad, por transformación.
- [Modelo de amenaza](https://www.afterpack.dev/es/docs/threat-model): la escala de adversarios de A0 a A5 y el eje estático frente a dinámico.
- [Protection Map](https://www.afterpack.dev/es/docs/protection-map): la vista por token que demuestra qué clase afectó a cada línea.
- [Buenas prácticas](https://www.afterpack.dev/es/docs/best-practices): qué marcar, qué dejar sin tocar y cómo rotar builds.
- [Niveles](https://www.afterpack.dev/es/docs/tiers): la frontera completa entre Free / Pro / Team / Enterprise.
- [Inicio rápido](https://www.afterpack.dev/es/docs/quickstart): instala el plugin y publica salida protegida en minutos.
- [Frameworks](https://www.afterpack.dev/es/docs/frameworks): el plugin para tu stack, o la vía de la CLI cuando no existe uno.
- [Configuración](https://www.afterpack.dev/es/docs/config): cada clave mencionada arriba y qué superficie puede establecerla.
- [Presets](https://www.afterpack.dev/es/docs/presets): la escala `minify`/`light`/`medium`/`hard`/`extreme`.
- [Directivas](https://www.afterpack.dev/es/docs/directives): la gramática de marcadores detrás de `preset=hard` y `skip`.
