TIPS 23 · CHIAVI

La clave en el código

El prompt que busca claves y contraseñas en el código y en el historial de git, cómo pasarlas a .env, y qué hacer si ya se te ha escapado una.

Qué es y por qué importa

Cuando conectas un servicio a tu app, como una base de datos, los pagos o una IA, te dan una clave. Es una cadena larga, y lo más cómodo es pegarla directamente en el código. Si se la pasas a Claude en el chat sin decirle dónde ponerla, a veces él también lo hace.

El problema es dónde acaba el código. Si lo subes a GitHub, la clave viaja con él. En un repositorio público la puede leer cualquiera, y hay programas que funcionan solo para recoger claves en cuanto aparecen. Quien la encuentra la usa en tu nombre.

Y borrarla del código no basta. Git guarda toda la historia: si la clave estuvo en un commit, aunque sea uno solo, aunque sea de hace meses, sigue ahí. Quien descarga el repositorio la vuelve a encontrar.

No todas las claves son secretas. La pública de Supabase o Firebase está hecha para estar en la app: hablo de ella en la guía PORTA (strikt.fit/porta). Aquí hablo de las otras: la clave secreta de la base de datos, la de la IA, la de los pagos, la de los correos. Esas están solo en el servidor, como en la guía LIMITE (strikt.fit/limite).

1. La revisión

Antes de arreglar, mira qué hay. Abre Claude Code en la carpeta del proyecto y pega:

El prompt
Busca en el proyecto y en todo el historial de git claves, tokens y contraseñas escritos en el código, también en los archivos de configuración y en los commits antiguos. Para cada uno dime archivo, línea (o commit) y servicio. No muestres nunca el valor.

«Todo el historial», si no Claude solo mira los archivos de ahora. «No muestres nunca el valor», porque te basta con saber dónde está: la clave no debe aparecer en el chat.

Una segunda opinión: gitleaks

Un programa hecho a propósito reconoce la forma de muchísimos tipos de clave. Yo uso gitleaks, que es de código abierto. En el Mac lo instalas con Homebrew:

La línea
brew install gitleaks

Luego, dentro de la carpeta del proyecto:

Los comandos
gitleaks git -v --redact
gitleaks dir -v --redact
  • gitleaks git revisa todo el historial, commit a commit.
  • gitleaks dir revisa los archivos de ahora, también los que git no sigue.
  • --redact oculta los valores en los resultados, -v muestra los detalles.

En GitHub

En los repositorios públicos el secret scanning de GitHub es gratuito y automático: si encuentra la clave de un servicio que conoce, pone un aviso en la pestaña Security and quality. La push protection va más allá: bloquea el push que lleva una clave, antes de que llegue a internet. Para tus propios push a repositorios públicos está activa por defecto; en el repositorio la activas desde Settings → Advanced Security. En los repositorios privados podría requerir una función de pago de GitHub.

2. Las claves en .env

El sitio correcto para una clave es un archivo .env que git no sube nunca. Funciona así:

  1. Las claves van en un archivo .env en la carpeta del proyecto: NOMBRE_CLAVE=valor.
  2. En el archivo .gitignore añade la línea .env: git no lo sube nunca.
  3. En el código queda solo el nombre: process.env.NOMBRE_CLAVE (tu framework o dotenv lee el archivo).
  4. Donde publicas la app, las mismas variables van en los ajustes del hosting.

El prompt para que lo haga Claude:

El prompt
Saca las claves del código: ponlas en .env, añade .env al .gitignore, crea un .env.example solo con los nombres y comprueba que ninguna clave secreta acabe en el código del navegador.

El .env.example, en cambio, sí lo subes: solo tiene los nombres, como AI_API_KEY= sin nada detrás. Así, más adelante, tú y quien trabaje contigo sabéis qué claves hacen falta sin ver ninguna.

Si .env ya estaba en el repositorio

El .gitignore solo vale para los archivos que git aún no sigue. Si .env ya se había subido, el .gitignore no basta: lanza git rm --cached .env, que lo quita del repositorio y lo deja en el ordenador, y luego haz un commit. Después cambia las claves que había dentro, porque se quedan en el historial: mira la parte 4. Si quieres, pide a Claude que lo haga y te explique cada paso.

3. Las variables que acaban en el navegador

Aquí es donde más se cae. Poner una clave en .env no la hace secreta: lo que cuenta es dónde se ejecuta el código que la usa. Lo que se ejecuta en el servidor sigue siendo tuyo. Lo que llega al navegador, o a la app en el móvil, lo ve cualquiera, con .env o sin él.

Algunos frameworks mandan a propósito al navegador las variables con cierto prefijo:

  • Next.js: NEXT_PUBLIC_. El valor se escribe dentro del JavaScript que llega al navegador.
  • Vite: VITE_. La documentación de Vite dice explícitamente que no pongas claves ahí.
  • Expo: EXPO_PUBLIC_. El valor queda en claro en la app compilada.

Una clave secreta con uno de estos prefijos es pública, aunque esté en .env. Pasa así: la llamada a la IA desde el navegador no funciona, se añade el prefijo, ahora funciona, y la clave ha salido. La solución correcta es llevar la llamada al servidor.

Si no quieres que Claude lea .env

Claude Code puede leer los archivos de la carpeta, .env incluido. Si prefieres que no, añade una regla de bloqueo en .claude/settings.json, en la carpeta del proyecto:

El archivo
{
  "permissions": {
    "deny": ["Read(./.env)"]
  }
}

Ponla después de mover las claves: bloquea también la escritura de ese archivo. Vale para las herramientas con las que Claude lee archivos y para comandos como cat, no para un script que abre el archivo por su cuenta: para eso hace falta la sandbox de Claude Code.

4. Si ya se te ha escapado una

Si encuentras una clave secreta en un commit, en GitHub o en el código que va al navegador, dala por quemada. No sabes quién la ha copiado ya. Hoy, en este orden:

  1. En la web del servicio revoca la clave y crea una nueva. Es el paso que cuenta: quien la ha copiado la puede seguir usando.
  2. Pon la nueva solo en .env y en los ajustes del hosting.
  3. Revisa en el servicio los consumos raros.
  4. Luego, con calma, limpia el historial.

Si la app está en línea puedes crear la clave nueva un momento antes de revocar la vieja, para que no se quede parada. Pero la vieja la revocas hoy, no mañana. Para el último paso, pide primero una explicación:

El prompt
Una clave ha acabado en un commit antiguo. Explícame cómo quitarla del historial de git antes de tocar nada.

Por qué este orden. Quitar una clave del historial significa reescribir la historia del repositorio (GitHub recomienda git-filter-repo) y subirla con un force push, y las copias antiguas y los forks todavía la contienen. Por eso GitHub pone como primer paso revocar la clave, y dice que a veces basta: una clave revocada ya no abre nada.

Errores comunes

  • Borrar la clave del código y quedarse ahí. Sigue en el historial: revócala y crea una nueva.
  • Limpiar el historial antes de revocar la clave. Revócala enseguida: la limpieza viene después.
  • Añadir .env al .gitignore cuando ya se había subido. Quítalo con git rm --cached .env y un commit, luego cambia las claves que había dentro.
  • Poner una clave secreta en una variable NEXT_PUBLIC_, VITE_ o EXPO_PUBLIC_. Acaba en el navegador o en la app: lleva la llamada al servidor.
  • Pegar las claves nuevas en el chat con Claude. Ponlas tú en .env y dile solo el nombre de la variable.

Lista de comprobación

  • Revisión hecha en archivos e historial, con Claude y con gitleaks.
  • Las claves secretas están solo en .env y en los ajustes del hosting.
  • .env está en el .gitignore y no está en el repositorio.
  • Hay un .env.example con los nombres, sin valores.
  • Ninguna clave secreta en variables NEXT_PUBLIC_, VITE_ o EXPO_PUBLIC_.
  • Cada clave escapada se ha revocado y sustituido.
  • Push protection activada en el repositorio de GitHub.

Preguntas frecuentes

Mi repositorio es privado. ¿Tengo que hacerlo igualmente?

Sí. Si un día lo haces público o añades a alguien, el historial llega entero, claves incluidas. Y las variables que acaban en el navegador son públicas de todas formas.

¿La clave pública de Supabase en una variable NEXT_PUBLIC_ está bien?

Sí, está hecha para estar en el navegador. Lo que protege los datos son las reglas de la base de datos, como explico en la guía PORTA. La clave secreta (secret o service_role), en cambio, nunca.

Ya he revocado la clave. ¿Tengo que limpiar el historial de todas formas?

No siempre: una clave revocada ya no abre nada. Si en el historial hay algo más delicado, como datos de personas, o no estás seguro de haberlo revocado todo, limpia, empezando por el prompt de la parte 4.

← Todas las guías