← Todos los checks
SeguridadSeveridad típica: Crítico

Detector de secretos en JavaScript

Busca claves de API y credenciales (Stripe, AWS, Supabase) filtradas en el JavaScript del navegador.

Qué comprueba

Este escaner revisa el JavaScript que tu web envia al navegador buscando patrones de claves de API y credenciales, como las de Stripe, AWS o Supabase. Detecta secretos que se incluyeron por error en el codigo del cliente. Analiza solo lo que ya es publico para cualquier visitante.

Por qué importa

Todo lo que va en el JavaScript del navegador es visible para cualquiera que abra las herramientas de desarrollo. Una clave secreta ahi puede usarse para acceder a tus servicios, gastar tu dinero o leer datos de tus usuarios. Es un error muy comun cuando se mezclan variables de servidor y de cliente.

Cómo se arregla

Mueve todas las claves secretas al servidor y accede a ellas solo desde el backend, nunca desde el codigo del navegador. Usa unicamente claves publicas o restringidas en el cliente, y rota cualquier secreto que ya se haya expuesto. En muchos frameworks, las variables sin el prefijo publico se quedan solo en el servidor.

Cómo funciona esta comprobación

Scanaris carga tu página principal, recoge los archivos JavaScript que enlaza desde tu propio dominio y los lee buscando patrones de credenciales conocidas: claves secretas y restringidas de Stripe (sk_live_, rk_live_), identificadores de acceso de AWS (AKIA…), claves de API de Google (AIza…), claves de OpenAI, tokens de GitHub (ghp_…) y claves privadas en formato PEM. Además decodifica cualquier JWT que encuentre y, si su carga lleva role: service_role, lo marca como clave de servicio de Supabase expuesta, que es crítico. Las claves públicas (pk_live_ de Stripe, la anon o publishable de Supabase, la configuración de Firebase) se excluyen a propósito: están diseñadas para ir en el navegador.

Qué hace un atacante con esto

Exactamente lo mismo que el escáner, pero a escala: hay robots que descargan el JavaScript de dominios nuevos en cuanto aparecen en los registros de certificados y prueban cada clave que encuentran. Una clave secreta de Stripe permite crear reembolsos o leer clientes; una de OpenAI, gastar tu cuota; una service_role de Supabase, leer y borrar toda tu base de datos saltándose las políticas. No hace falta "hackear" nada: el archivo es público por definición, lo recibe cada visitante.

Por qué acaba una clave en el bundle

Casi siempre por una variable de entorno con el prefijo público: NEXT_PUBLIC_ en Next.js, VITE_ en Vite, NUXT_PUBLIC_ en Nuxt. El framework incrusta esas variables en el JavaScript en el momento del build, que es justo lo que quieres para una clave pública y justo lo que no quieres para una secreta. El otro camino habitual es pegar la clave en el chat de Lovable, Bolt o Cursor para "conectar" un servicio: el modelo la escribe en el componente que la necesita. Más detalle en secretos que tu app filtra en el navegador.

Cómo arreglarlo

  • Rota la clave primero. Dala por comprometida aunque la quites del código: los bundles anteriores siguen en cachés y en el historial de despliegues. Genera una nueva en el panel del servicio y revoca la antigua.
  • Mueve la llamada al servidor. En Next.js, a un Route Handler o una Server Action; en Nuxt, a una ruta de server/api; con Supabase, a una Edge Function; en Netlify, a una función. El navegador llama a tu servidor y el servidor, con la clave, al servicio.
  • Quita el prefijo público a la variable y guárdala en el panel de tu hosting como variable de servidor. Si una variable necesita el prefijo, es que es pública: pregúntate si debería serlo.
  • Revisa el historial de git. Si la clave se subió alguna vez a un repositorio, está en el historial aunque el archivo actual esté limpio.

Comprueba que está limpio

curl -s https://tudominio.com | grep -oE 'src="[^"]+\.js[^"]*"' | cut -d'"' -f2 | head -5
curl -s https://tudominio.com/assets/index-XXXX.js | grep -oE "sk_live_[A-Za-z0-9]+|rk_live_[A-Za-z0-9]+|AKIA[A-Z0-9]{16}|AIza[A-Za-z0-9_-]{35}|ghp_[A-Za-z0-9]{36}|BEGIN (RSA |EC )?PRIVATE KEY" | head

El primer comando lista tus scripts; el segundo busca los patrones en uno de ellos. Si no sale nada en ninguno, el bundle está limpio. Repite tras cada despliegue grande, porque una regeneración con IA puede volver a meter la clave.

Preguntas frecuentes

¿La clave anon de Supabase o la pk_live de Stripe son un problema?

No. Son públicas por diseño y el escáner no las marca. Lo que protege tus datos con la anon es RLS; lo que protege Stripe con la pk_live es que solo sirve para crear tokens de pago, no para cobrar.

Me marca una clave de Google que uso para Maps. ¿Es grave?

Las claves AIza pueden ser legítimamente públicas si están restringidas por dominio y por API en la consola de Google Cloud. Sin restricciones, cualquiera puede gastar tu cuota. Restríngela y el riesgo real desaparece, aunque el patrón siga apareciendo.

Ya la quité del código y sigue saliendo.

El bundle desplegado puede ser el antiguo, o la clave puede estar en otro archivo (un chunk compartido, un script de terceros alojado en tu dominio). Despliega de nuevo, vuelve a escanear y, en cualquier caso, rota la clave.

Compruébalo en tu web

Escanea tu web gratis y mira si este y otros 44 checks pasan, con un prompt de arreglo por cada fallo.

Escanear mi web gratis

Del blog

Checks relacionados