Qué encuentra un escáner de seguridad en tu bundle de JavaScript

Patrones con forma de secreto, un sourceMappingURL que lleva a tu código fuente, huellas de librerías y una etiqueta de texto plano o minificado. Qué comprueba un lector automatizado y qué cambia un build ofuscado en el informe.

Un escáner, el informe de un pentest o el cuestionario de seguridad de un cliente ha preguntado si el JavaScript que publicas es legible, y necesitas una respuesta.

Qué comprueba realmente un escáner automatizado

Un escáner no lee tu código como lo hace una persona. Ejecuta un conjunto fijo de comprobaciones sobre el texto que pueda sacar de la respuesta: busca patrones con forma de secreto en tus literales de cadena, sigue un comentario sourceMappingURL hasta tu código fuente original cuando lo hay, identifica las versiones de las librerías que incluiste en el bundle y etiqueta el archivo como texto plano o minificado.

Nada de eso requiere entender tu lógica. Una coincidencia de patrón encuentra una clave tanto si el código que la rodea llega a ejecutarse como si no. Un source map convierte un escaneo en una lectura completa del código fuente con una sola solicitud. Quien lee el informe ve la etiqueta, y una etiqueta de texto plano o minificado le dice que el código fuente está ahí mismo.

Qué cambia un build ofuscado en el informe

Los literales se codifican por defecto, así que el texto que busca una comprobación de patrones de secretos no está en el archivo. strings.leaks.max hace fallar el build cuando sobrevive un literal legible, y lo señala con DIAG_SURVIVING_READABLE_LITERAL. Un build de producción no escribe ningún archivo .map ni ningún comentario sourceMappingURL salvo que los pidas con sourceMap.enabled y sourceMap.emitUrl, así que no hay ningún map que seguir hasta el código fuente.

Una comprobación del nivel de protección sigue informando de que el archivo está ofuscado, que es para lo que sirve esa comprobación. La forma de la salida es visible para cualquiera que la mire; tu lógica y tus cadenas son lo que deja de ser legible.

Un build de versión que responde al cuestionario

Un build de versión que codifica sus cadenas y deja el map fuera de la salida:

{
  "preset": "hard",
  "strings": {
    "encode": true,
    "leaks": { "max": 0 }
  },
  "sourceMap": { "enabled": false }
}

sourceMap.enabled ya está desactivado en un build de producción. Establecerlo aquí hace que la respuesta sea la misma en todas las máquinas que ejecuten el build.

Escanea primero tu propio despliegue

Ejecuta contra tu propio despliegue las comprobaciones que hace un escáner, antes de que lo haga un cliente:

$ npx afterpack@latest audit https://your-site.example.com

afterpack audit busca primero secretos y source maps expuestos, y después el nivel de protección, los patrones peligrosos, el stack detectado y las CVE conocidas en lo que puede identificar. Para un pipeline, añade --diagnostics.format=json para poder parsear el resultado en lugar de extraerlo del texto, y --diagnostics.level para elegir cuánto detalle devuelve. Ejecútalo contra cada entorno en el que despliegues, staging incluido.

Mantén protectionMap.enabled activado en local. El Protection Map relaciona cualquier cosa que encuentre la auditoría con la región del código fuente de la que procede.

Qué no significa un escaneo limpio

Auditar tu propio sitio te dice lo que cualquier lector ya puede sacar de él, y un build con literales codificados y sin map publicado contiene mucho menos texto legible que el código fuente a partir del cual se compiló.

La ofuscación no arregla lo que encontró un escaneo. Una dependencia vulnerable, un bug de inyección o una credencial que ya salió siguen activos en la aplicación en ejecución después del build, así que corrígelos en el código fuente y rota la credencial.

Siguiente

  • Auditoría: la referencia del comando y cómo leer los resultados.
  • Modelo de amenaza: la frontera entre un lector estático y alguien que ejecuta tu código.
  • Diagnósticos: cada código que puede emitir un build o una auditoría.