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.

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 defiendeEl ataque que queda descartadoLa propiedad que hace el trabajo
La legibilidad de tu bundle enviadoPegar el bundle en una herramienta o un modelo y recuperar tu código fuenteLos 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 bundleEscribir una vez un desofuscador, una firma o un script de parche y reutilizarlo entre releasesUna 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ónDemostrar que un bundle enviado es tu código, tu librería o tu algoritmoUn 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 comparasHacer grep en un bundle en busca de un token o una cadena de puertaEl mínimo de cadenas, y para una puerta de igualdad, la no materialización. En light, los números siguen legibles
La integridad del artefacto en usoParchear una comprobación y enviar el build parcheadoUna seed nueva mueve el punto de parcheo en cada release. La autointegridad 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 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ñoAtacanteAlcanceCoste para el atacanteResultado
A0Herramientas estáticas de patrones: grep, matchers de AST, pasadas deterministas de desofuscaciónun buildTrivialRecupera lo que queda en texto plano. Los desofuscadores existentes no añaden nada sobre grep (resultados medidos).
A1Un modelo a ciegas: una sola lectura, sin ejecución, sin herramientasun buildTrivialRevirtió 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).
A2Un modelo agéntico con ejecución, herramientas y un presupuestoun buildBajo, por buildCae 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.
A3Un modelo entrenado sobre una muestra grande de la propia salida de AfterPacktoda la distribución, no un buildInversión única, luego despreciable por buildNo medido. Ver más abajo.
A4Análisis dinámico instrumentado: built-ins interceptados, ejecución grabada, capturas de heapun build, repetible con poco costeTiempo de analista, por buildCae para las cadenas: un hook de runtime recuperó 96 de 97 en todos los presets (resultados medidos). Consulta el límite.
A5Un runtime privilegiado: navegador parcheado, hooks nativos por debajo de la capa de JavaScriptun buildConfiguración inicial alta, luego despreciableCae, 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 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 aplica un preset más pesado a las regiones que marcas y añade dos tipos de transformación opcionales, transforms.selfIntegrity.enabled y transforms.comparisonHardening.enabled. Marca lo que realmente importa; consulta Buenas prácticas.

A4 y A5 abandonan por completo el artefacto estático. El límite 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.

Atacantelightmediumhard y extreme
Herramientas de patrones: webcrack, restringer, synchrony, terserNada más que grepNada más que grepNada más que grep
Grep de 42 números plantados421818
Evaluación estática de la salida92–100% de las cadenas96–98% de las cadenas, tras una pasada de limpieza que conserva el comportamiento96–98% de las cadenas, tras la misma pasada
Un hook de 15 líneas sobre un built-in96 de 97 cadenas96 de 97 cadenas96 de 97 cadenas
Un modelo de frontera, a ciegas, sin herramientas ni ejecuciónLos 3 programas revertidos por completo2 de 3 revertidos por completo; el tercero cedió 12 de 17 secretos sin una reconstrucción que se ejecuteSolo 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 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 divide esto en las clases a las que se enfrenta un lector estático.

La 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 y Builds y CI 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. El ajuste que la aporta, transforms.comparisonHardening.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. 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 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 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 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, 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 lo deja desactivado y en su lugar eleva una región al objetivo de complejidad 12 con el mínimo de cadenas 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 es el único lugar donde AfterPack declara qué viene.

ControlQué aporta AfterPack hoyQué 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. 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 y polimórfico por build 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 analiza un sitio en producción en busca de lo que ya está filtrando.
Decisiones de confianza sobre el clienteEl polimorfismo por build convierte un parche de una sola vez en un coste recurrente de re-derivación.Validación con autoridad del servidor, 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, y es real: eleva el coste recurrente de atacar lo que sí se ejecuta en el cliente.

Siguiente