# Modelo de amenaza de AfterPack: la superficie defendida

El documento de revisión de seguridad de AfterPack: el activo que defiende, qué recupera cada clase de atacante, cómo sobrevive un secreto, dónde termina el alcance actual y los controles junto a los que funciona.

Source: https://www.afterpack.dev/es/docs/threat-model

AfterPack defiende un único activo: **el artefacto JavaScript que emite tu build**. Lo reescribe para que leerlo, identificarlo o parchearlo le cueste al atacante trabajo real, y para que las herramientas construidas contra un build fallen en el siguiente.

Esta página está escrita para leerse en una revisión de seguridad. Indica la superficie defendida, qué recupera cada clase de atacante, el punto en que un control en tiempo de build deja de sostenerse y los controles que cubren el resto.

## La superficie defendida

| Qué se defiende | El ataque que queda descartado | La propiedad que hace el trabajo |
|---|---|---|
| La legibilidad de tu bundle enviado | Pegar el bundle en una herramienta o un modelo y recuperar tu código fuente | Los identificadores se renombran, las cadenas se codifican, el flujo de control se aplana y las identidades se fusionan, así que leer el bundle de vuelta hasta tu código fuente exige un trabajo real que se renueva en cada build |
| La *estructura* de tu bundle | Escribir una vez un desofuscador, una firma o un script de parche y reutilizarlo entre releases | Una [`seed`](https://www.afterpack.dev/es/docs/config#seed) nueva por build, así que las herramientas escritas contra los patrones de un build fallan en el siguiente |
| La atribución de tu implementación | Demostrar que un bundle enviado es tu código, tu librería o tu algoritmo | Un lector estático debe rehacer en cada build el trabajo de recuperación de renombrado y codificado antes de poder cotejar el resultado con el tuyo; las clases que destruyen la procedencia y eliminarían esa coincidencia de raíz no se producen hoy |
| El material de cadenas contra el que comparas | Hacer grep en un bundle en busca de un token o una cadena de puerta | El [mínimo de cadenas](https://www.afterpack.dev/es/docs/config#strings-encode), y para una puerta de igualdad, la [no materialización](#protecting-a-secret). En `light`, los números siguen legibles |
| La integridad del artefacto en uso | Parchear una comprobación y enviar el build parcheado | Una [`seed`](https://www.afterpack.dev/es/docs/config#seed) nueva mueve el punto de parcheo en cada release. La [autointegridad](#anti-tamper) solo detecta cambios en su propia tabla de comprobación |

Todo lo que sigue matiza estas cinco filas: a quién hay que vencer, hasta dónde llega cada propiedad y hasta dónde no llega.

## Clases de atacante

Dos ejes, no una sola escala. Un **[atacante estático](https://www.afterpack.dev/es/docs/reversal-classes#static-attacker-defined)** lee los bytes: grep, herramientas de AST, un paso de desofuscador o un modelo de última generación que explica código sin ejecutarlo. Un **atacante dinámico** ejecuta el artefacto: bajo un runtime parcheado, con los built-ins interceptados o contra una traza de ejecución grabada y reproducida fuera de línea. Una transformación puede ser fuerte contra un lector y débil contra quien lo ejecuta, y confundir ambos es la sobreafirmación más común en esta categoría.

La escalera de abajo ordena por capacidad del atacante. El coste es relativo a las demás filas, no una cifra absoluta.

| Peldaño | Atacante | Alcance | Coste para el atacante | Resultado |
|---|---|---|---|---|
| **A0** | Herramientas estáticas de patrones: grep, matchers de AST, pasadas deterministas de desofuscación | un build | Trivial | Recupera lo que queda en texto plano. Los desofuscadores existentes no añaden nada sobre grep ([resultados medidos](#measured-results)). |
| **A1** | Un modelo a ciegas: una sola lectura, sin ejecución, sin herramientas | un build | Trivial | Revirtió la mayor parte de lo que leyó: sobre tres programas pequeños, revirtió por completo los tres en `light`, dos en `medium` y uno en `extreme`, y donde no pudo reconstruir uno que se ejecute, aun así recuperó la mayoría de los secretos plantados ([resultados medidos](#measured-results)). |
| **A2** | Un modelo agéntico con ejecución, herramientas y un presupuesto | un build | Bajo, por build | Cae para ese build: ejecutar el código expone los valores que usa. Las herramientas construidas sobre la forma de ese build hay que rehacerlas para el siguiente. |
| **A3** | Un modelo entrenado sobre una muestra grande de la propia salida de AfterPack | toda la distribución, no un build | Inversión única, luego despreciable por build | No medido. Ver más abajo. |
| **A4** | Análisis dinámico instrumentado: built-ins interceptados, ejecución grabada, capturas de heap | un build, repetible con poco coste | Tiempo de analista, por build | Cae para las cadenas: un hook de runtime recuperó 96 de 97 en todos los presets ([resultados medidos](#measured-results)). Consulta [el límite](#the-boundary). |
| **A5** | Un runtime privilegiado: navegador parcheado, hooks nativos por debajo de la capa de JavaScript | un build | Configuración inicial alta, luego despreciable | Cae, para todo lo que corre en el cliente. |

Los peldaños son acumulativos en capacidad, no en probabilidad. A0 cubre la abrumadora mayoría de los ataques reales sobre JavaScript enviado: alguien ejecuta una herramienta gratuita o pega tu bundle en una ventana de chat. A5 es el desenlace raro y costoso que la mayoría de los productos nunca enfrenta.

**De A0 a A2 atacan un build.** Una [`seed`](https://www.afterpack.dev/es/docs/config#seed) nueva por build, salvo que fijes una, cambia la forma, así que un reconocedor o un parche escrito contra un build hay que reescribirlo para el siguiente. No oculta valores: todo lo que el código usa, quien lo ejecuta puede leerlo.

**A3 ataca toda la distribución de salida.** Un modelo entrenado sobre una muestra suficientemente grande aprende a ser invariante a la varianza por build, y rotar tu semilla no le cuesta nada. Lo que sigue ayudando es la puntería: [Pro](https://www.afterpack.dev/es/docs/pro#what-pro-buys) aplica un preset más pesado a las [regiones que marcas](https://www.afterpack.dev/es/docs/directives#what-you-can-set) y añade dos tipos de transformación opcionales, [`transforms.selfIntegrity.enabled`](https://www.afterpack.dev/es/docs/config#transforms-kind-enabled) y [`transforms.comparisonHardening.enabled`](https://www.afterpack.dev/es/docs/config#transforms-kind-enabled). Marca lo que realmente importa; consulta [Buenas prácticas](https://www.afterpack.dev/es/docs/best-practices).

**A4 y A5 abandonan por completo el artefacto estático.** [El límite](#the-boundary) cubre ambos.

### Resultados medidos

Lo que cada atacante recuperó de la salida del motor 0.2.2, medido en octubre de 2026 sobre cinco programas con secretos plantados y sobre bundles reales de producción. La fila del modelo de frontera es una medición aparte, de octubre de 2026, sobre el motor 0.2.6.

| Atacante | `light` | `medium` | `hard` y `extreme` |
|---|---|---|---|
| Herramientas de patrones: webcrack, restringer, synchrony, terser | Nada más que grep | Nada más que grep | Nada más que grep |
| Grep de 42 números plantados | 42 | 18 | 18 |
| Evaluación estática de la salida | 92–100% de las cadenas | 96–98% de las cadenas, tras una pasada de limpieza que conserva el comportamiento | 96–98% de las cadenas, tras la misma pasada |
| Un hook de 15 líneas sobre un built-in | 96 de 97 cadenas | 96 de 97 cadenas | 96 de 97 cadenas |
| Un modelo de frontera, a ciegas, sin herramientas ni ejecución | Los 3 programas revertidos por completo | 2 de 3 revertidos por completo; el tercero cedió 12 de 17 secretos sin una reconstrucción que se ejecute | Solo `extreme`: 1 de 3 revertido por completo (9 de 9 secretos); los otros cedieron 12 de 17 y 2 de 3 secretos sin reconstrucciones que se ejecuten |

En la fila del modelo de frontera, Claude Opus 5.5 recibió solo el archivo ofuscado y los 5 archivos que rechazó pasaron a Claude Opus 4.8; «revertido por completo» significa que el código reconstruido imprime los mismos vectores de prueba que el original y contiene todos los secretos plantados. Salvedades: un corpus pequeño de tres programas, un intento por programa y preset, y Claude como único atacante.

## Qué protege la ofuscación

AfterPack persigue dos propiedades. La primera es el **coste**: revertir un build debería costar más que lo que protege. La segunda es una **ventaja de ritmo de releases**: tu ritmo de actualización supera la velocidad con que las herramientas del atacante se generalizan al siguiente build. Ningún ofuscador, AfterPack incluido, hace imposible la reversión, y nada en esta página debe leerse como si lo afirmara.

El [preset por defecto](https://www.afterpack.dev/es/docs/presets#the-default-is-light) renombra los identificadores y codifica los literales de cadena. A partir de `medium`, AfterPack hace más que eso: el flujo de control se aplana, las identidades se fusionan y la lógica se reescribe en formas equivalentes que las herramientas estándar no pueden deshacer. Un atacante que revierte esa salida puede recuperar un programa que funciona, pero solo después del trabajo real de reconocimiento de patrones que cuestan estas transformaciones, y una herramienta escrita contra las formas de un build se rompe en el siguiente. [Clases de reversión](https://www.afterpack.dev/es/docs/reversal-classes) divide esto en las clases a las que se enfrenta un lector estático.

La [`seed`](https://www.afterpack.dev/es/docs/config#seed) por build es aleatoria por defecto, así que dos builds del mismo código salen estructuralmente distintos en todas partes. Las herramientas escritas contra los patrones del build N fallan en el build N+1, mientras que los valores que alguien ya leyó del build N siguen leídos. Esa propiedad solo compensa si rotas los builds; [Buenas prácticas](https://www.afterpack.dev/es/docs/best-practices) y [Builds y CI](https://www.afterpack.dev/es/docs/builds) explican cómo integrar las recompilaciones en un pipeline de releases.

Nada de esto oculta un secreto por sí solo. Eso requiere un mecanismo distinto.

## Proteger un secreto

Un secreto sale de cualquier programa que se comporta como el tuyo, incluido el que un lector estático recupera de una región protegida. Remodelar el código alrededor de un secreto no hace nada por él.

Lo que protege un secreto frente a alguien que lee los bytes es la **no materialización**, cubierta en [Cómo sobreviven los secretos](https://www.afterpack.dev/es/docs/concepts#how-secrets-survive). El ajuste que la aporta, [`transforms.comparisonHardening.enabled`](https://www.afterpack.dev/es/docs/config#transforms-kind-enabled), es un ajuste Pro, desactivado por defecto, y funciona igual en cada nivel de preset.

Frente a alguien que ejecuta tu código, se aplica [el límite](#the-boundary). La varianza por build eleva el coste de volver a extraer un secreto en cada rotación. Un hook genérico de runtime dirigido al motor de JavaScript cuesta lo mismo sin importar esto, ya que no depende de la estructura de AfterPack. [Cómo funciona AfterPack](https://www.afterpack.dev/es/docs/concepts#how-secrets-survive) sitúa el mismo mecanismo junto a lo que ya hace la salida estándar.

## El límite

Un atacante que ejecuta tu código bajo un runtime instrumentado puede alcanzar cualquier valor que el propio código use. Si el programa en ejecución lo necesita, un atacante dinámico decidido puede observarlo. AfterPack hace que localizar ese valor sea costoso y que [rotar](https://www.afterpack.dev/es/docs/config#seed) para alejarse de él sea barato. No lo hace irrecuperable.

> **Mantén la autoridad del lado del servidor fuera del cliente**
>
> Las claves de firma, las credenciales de larga duración y cualquier cosa que un atacante pudiera reutilizar o revender pertenecen al servidor, sea cual sea el ofuscador que uses.

Este límite no es exclusivo de AfterPack. [Todo ofuscador en tiempo de build](https://www.afterpack.dev/es/docs/comparison) choca con el mismo muro en cuanto un atacante controla el runtime. Lo que AfterPack conserva más allá de ese muro es la forma del siguiente release: las herramientas construidas contra un build hay que rehacerlas para el siguiente.

## Anti-manipulación

La autointegridad, [`transforms.selfIntegrity.enabled`](https://www.afterpack.dev/es/docs/config#transforms-kind-enabled), añade una comprobación sobre su propia tabla de comprobación. Solo detecta un cambio en esa tabla. Un cambio en cualquier otra parte de tu código, como un `return true` parcheado, no se detecta.

Tampoco detecta la observación pasiva: un hook que registra valores, una lectura de heap o la instrumentación nativa no cambian nada de lo que comprueba. Es un ajuste de Pro, desactivado por defecto y global, y necesita código en `medium` o superior al que adjuntarse, así que un build `light` solo lo lleva dentro de una región elevada a ese nivel. [`preset=hard`](https://www.afterpack.dev/es/docs/config#preset) lo deja desactivado y en su lugar eleva una región al [objetivo de complejidad](https://www.afterpack.dev/es/docs/config#complexity) 12 con el [mínimo de cadenas](https://www.afterpack.dev/es/docs/config#strings-encode) activado.

## Qué no reemplaza AfterPack

AfterPack es un control dentro de una pila, y una revisión de seguridad debería ver dónde están sus límites. Cada fila de abajo indica el alcance actual del control en tiempo de build y el control que cubre el resto. El alcance cambia a medida que el producto avanza; el [roadmap público](https://www.afterpack.dev/es/roadmap) es el único lugar donde AfterPack declara qué viene.

| Control | Qué aporta AfterPack hoy | Qué cubre el resto |
| --- | --- | --- |
| **Defensa en la capa de peticiones** (bots, scraping, abuso) | Un scraper que quiere datos estructurados de tu bundle tiene que [revertirlo primero](https://www.afterpack.dev/es/docs/reversal-classes). AfterPack actúa sobre el artefacto y no ve la petición. | Un servicio de desafío-respuesta (reCAPTCHA, hCaptcha, Arkose) más limitación de tasa del lado del servidor por IP, sesión o clave de API. |
| **Integridad del cliente** (trampas, automatización, clientes parcheados) | Un autor de trampas o automatización que trabaja contra un cliente [`preset=hard`](https://www.afterpack.dev/es/docs/config#preset) y [polimórfico por build](https://www.afterpack.dev/es/docs/config#seed) tiene que rehacer un análisis real en cada release en vez de parchear un offset conocido una sola vez. AfterPack eleva ese coste; no lo arbitra. | Validación con autoridad del servidor de impactos, transiciones de estado y resultados, con el cliente usado solo para representar decisiones que el servidor ya tomó. |
| **Integridad de la entrega** (proxies, CDNs, extensiones) | AfterPack protege los bytes que produce. No controla la ruta que recorren, y una extensión que usa la propia API de bloqueo de peticiones del navegador decide qué cargar antes de que se ejecute ningún JavaScript. | Subresource Integrity (`integrity="sha384-…"`) para que el navegador rechace un bundle que no coincida con el hash que construiste, más validación del lado del servidor de cualquier cosa que un intermediario pudiera influir. |
| **Seguridad de la aplicación** (bugs, inyección, autenticación rota) | La ofuscación ralentiza encontrar un fallo leyendo el código fuente. No elimina el fallo, y cualquiera que llegue a él a través del programa en ejecución nunca lee tu código fuente en absoluto. | Arregla el bug. [`npx afterpack@latest audit`](https://www.afterpack.dev/es/docs/audit) analiza un sitio en producción en busca de lo que ya está filtrando. |
| **Decisiones de confianza sobre el cliente** | El [polimorfismo por build](https://www.afterpack.dev/es/docs/config#seed) convierte un parche de una sola vez en un coste recurrente de re-derivación. | [Validación con autoridad del servidor](#not-a-substitute-for-server-authoritative-validation), más abajo. |

### No sustituye a la validación con autoridad del servidor

Cada fila de arriba comparte una causa raíz: una comprobación de licencia, una puerta de pago, una regla de derecho de acceso o un feature flag que de verdad debe sostenerse es una decisión que un cliente toma sobre sí mismo, y quien controla ese cliente puede falsificarla. Esto es una propiedad de la ejecución del lado del cliente, no un hueco de ningún ofuscador en particular, y ninguna transformación en tiempo de build lo cierra.

Para cualquier cosa que deba sostenerse frente a un cliente hostil, como licencias, pagos, derechos de acceso o resultados de partida, toma la decisión del lado del servidor y deja que el cliente solo la muestre. La contribución de AfterPack es [económica](#what-obfuscation-protects), y es real: eleva el coste recurrente de atacar lo que sí se ejecuta en el cliente.

## Siguiente

- [Qué puede recuperar un atacante](https://www.afterpack.dev/es/docs/reversal-classes): las clases de atacante estático en profundidad, con el diagrama.
- [Cómo funciona AfterPack](https://www.afterpack.dev/es/docs/concepts): qué le hace ya la salida estándar a tu bundle.
- [Protection Map](https://www.afterpack.dev/es/docs/protection-map): verifica qué clase alcanzó qué línea en tu propio build.
- [Buenas prácticas](https://www.afterpack.dev/es/docs/best-practices): qué marcar, qué rotar y qué mantener del lado del servidor.
- [Privacidad y manejo de datos](https://www.afterpack.dev/es/docs/privacy): qué sale de tu máquina, qué se almacena y durante cuánto tiempo.
- [Auditoría](https://www.afterpack.dev/es/docs/audit): analiza una URL en producción en busca de las filtraciones que la ofuscación no puede deshacer.
- [Comparativa](https://www.afterpack.dev/es/docs/comparison): en qué se diferencia AfterPack de las herramientas que ya conoces.
- [Niveles](https://www.afterpack.dev/es/docs/tiers): qué peldaños ya cubre Free y qué añade Pro.
