Ilustración abstracta de pintura fluida en tonos turquesa, cian y blanco para la noticia: Un fichero de permisos de Claude Code expone 1.379 secretos en GitHub

IA

Un fichero de permisos de Claude Code expone 1.379 secretos en GitHub

Claude Code guarda en settings.local.json los comandos aprobados con sus contraseñas y Apiiro halló 1.122 copias públicas con secretos en GitHub.

Un desarrollador pide a Claude Code que lance un volcado de su base de datos, el agente muestra el comando con la contraseña dentro y pregunta si puede ejecutarlo. Si la respuesta es la segunda opción del menú, Yes, and don't ask again, ese comando se escribe tal cual, contraseña incluida, en el fichero .claude/settings.local.json del proyecto, y Apiiro encontró el 5 de noviembre de 2026 que 1.122 copias públicas de ese fichero en GitHub contienen al menos un secreto, 1.379 secretos distintos en total.

De la opción Yes, and don't ask again al fichero settings.local.json

Claude Code pide permiso antes de ejecutar la mayoría de comandos de shell. El prompt ofrece cuatro salidas, como muestra la captura de la documentación de permisos de Anthropic: aprobar una vez, aprobar y no volver a preguntar, pasar a modo automático o rechazar.

Prompt de permisos de Claude Code con la opción Yes, and don't ask again para un comando Bash
Fuente: Anthropic, «Configure permissions»

La segunda opción guarda una regla allow en .claude/settings.local.json, en la raíz del repositorio git. Si el comando aprobado es un prefijo (npm test *), la regla guarda el patrón. Si es un comando exacto, la regla guarda la cadena completa, y ahí es donde viaja el secreto. Un fichero con dos aprobaciones de ese tipo tiene el aspecto del bloque siguiente.

{
  "permissions": {
    "allow": [
      "Bash(npm test *)",
      "Bash(PGPASSWORD=Sup3rS3cr3t! pg_dump -h db.example.internal -U app app > backup.sql)"
    ]
  }
}

La documentación de ajustes explica que el fichero es personal y que la primera vez que Claude Code lo escribe en un repositorio que no lo ignora ya, añade la ruta al fichero global de exclusiones de git del usuario, el que apunta core.excludesFile o, en su defecto, ~/.config/git/ignore. Anthropic lo resume con una frase: «You don't need to gitignore it yourself, unless you created it by hand» (no hace falta que lo ignores tú, salvo que lo hayas creado a mano). Esa protección vive en el equipo del desarrollador, no en el proyecto. No cubre a quien clona el repositorio en otra máquina, a quien ya había hecho commit del fichero antes ni a quien lo creó a mano, y Claude Code nunca lo añade al .gitignore del propio repositorio. Según The Hacker News, los investigadores reprodujeron el comportamiento con los modelos Opus 5.5 y Fable 5.1 y compararon la documentación de 16 agentes de programación: Claude Code es el único que guarda por defecto el comando aprobado completo en un fichero del proyecto.

1.379 secretos en 1.122 copias públicas y 501 que abren una base de datos

Shmulik Cohen y Matan Giladi, de Apiiro, firman el informe del 5 de noviembre. Localizaron 104.185 copias públicas de settings.local.json en 103.617 repositorios de GitHub y clasificaron como secreto real lo que tenía formato y contexto de credencial, con un revisor automático de IA confirmando cada candidato. El 1,1 % de las copias, 1.122, tenía al menos uno. El gráfico del informe, reproducido por The Hacker News, desglosa los 1.379 valores distintos por tipo.

Gráfico de barras de Apiiro con los 1.379 secretos de settings.local.json repartidos por tipo de credencial
Fuente: Apiiro, «Coding Agents Copy Your Secrets to a Hidden File. Thousands Are on GitHub», vía The Hacker News

Las dos barras en verde son las que preocupan a cualquier responsable de datos: 379 contraseñas de bases de datos y 122 claves service-role de Supabase, que junto a otras suman 501 secretos que abren una base de datos sin más pasos. Siguen 398 tokens de API de servicios, 149 claves de API de modelos de IA, 110 claves de API de Google, 103 tokens de cuenta de Supabase, 43 accesos SSH a servidores públicos, 42 tokens de GitHub, GitLab y npm y 23 claves de AWS. Apiiro no probó ninguno de ellos, pero su resumen deja dos datos que pesan. El primero, «One confirmed its key had been live» (una organización confirmó que su clave había estado activa). El segundo, el reparto por propietarios, 63 organizaciones, casi todas empresas de software. Entre los ejemplos que cita hay una clave cloud con permisos de administrador de un fabricante de bases de datos, una contraseña de Azure Postgres de producción, un token de producción de Square y la contraseña de propietario de una base de datos de historiales médicos. Las 149 claves de modelos de IA encajan con lo que ya vimos cuando LiteLLM 1.107.0 robó credenciales de IA y nube desde PyPI: son el tipo de secreto que más rápido se monetiza.

Recuento de Apiiro

Cifra

Copias públicas de settings.local.json

104.185 en 103.617 repositorios

Copias con al menos un secreto

1.122 (1,1 %)

Secretos distintos en copias actuales

1.379

Secretos solo en el historial de git

734 más, en 509 repositorios

Forks que arrastran los mismos secretos

211 forks de 74 repositorios

Copias del settings.json compartido con secretos

298 de 76.011

Total con historial, fichero compartido y transcripciones

2.539 secretos en 1.994 repositorios

Organizaciones afectadas

63

Según los datos que publica The Hacker News, el lunes 2 de noviembre todos los ficheros con secreto seguían en la rama por defecto de su repositorio, y en la mitad de los casos llevaban allí al menos 217 días. En 539 de los 1.105 repositorios rastreados, el commit que cambió el fichero lleva a Claude como coautor, lo que para los investigadores indica que a menudo es el propio agente quien lo sube. Apiiro avisó a los propietarios el 2 y el 3 de noviembre, y lo hizo a la vista: en repositorios sin contacto privado abrió issues públicos como el que muestra la captura, con la petición de rotar el secreto y dejar de versionar el fichero.

Issue abierto por Apiiro en GitHub el 2 de noviembre pidiendo rotar un secreto de settings.local.json
Fuente: Apiiro, «Security: a secret is committed to this repository», issue 26 en GitHub

El mensaje termina con una frase que conviene leer con el matiz de que Apiiro no verificó los secretos: «We have not used it» (no lo hemos usado). Quien sí puede haberlo usado es cualquiera que rastree GitHub, y los 211 forks con las mismas credenciales demuestran que borrar el fichero del repositorio original no deshace nada.

Un aviso de agosto de 2025 cerrado por un bot y dos recuentos que apuntaban igual

El comportamiento no es nuevo ni desconocido para Anthropic. El 11 de agosto de 2025, un usuario abrió en el repositorio de Claude Code el issue 5544 con el título approved bash commands containing credentials are stored as is with plain text credentials in settings.local.json. Pedía algo concreto: «Sensitive credentials should be sanitized, redacted, or excluded from the auto-approval list» (las credenciales sensibles deberían sanearse, ocultarse o excluirse de la lista de aprobación automática). El issue recibió las etiquetas bug y area:security, un aviso de inactividad del bot de GitHub Actions el 3 de diciembre de 2025, el cierre como not planned el 3 de enero de 2026 y el bloqueo de comentarios el 10 de enero. Ningún empleado de Anthropic escribió en él.

Texto del issue 5544 de Claude Code en GitHub que describe credenciales en texto plano en settings.local.json
Fuente: GitHub, issue 5544 del repositorio anthropics/claude-code

The Hacker News pidió comentarios a Anthropic para su pieza del 7 de noviembre y no obtuvo respuesta. Sobre los cuatro avisos relacionados que Apiiro cita en GitHub, el medio es tajante: «None got a reply from Anthropic» (ninguno recibió respuesta de Anthropic). En el momento de escribir esta noticia, el 7 de noviembre de 2026 a las 18:30 UTC, Anthropic no había publicado ningún comunicado ni cambio documentado sobre el guardado de comandos con credenciales.

Tampoco es la primera vez que el fichero aparece donde no debía. El 27 de abril de 2026, BD Tech Talks recogió un rastreo de Lakera sobre unos 46.500 paquetes del registro npm: 428 incluían un settings.local.json y 33 ficheros de 30 paquetes contenían credenciales, con tokens de npm, de GitHub, de Telegram y de Hugging Face entre ellas. SecurityBrief atribuyó aquel mismo recuento a Check Point, y es Steve Giguere, Principal AI Security Advocate de Check Point, quien lo resumió en la pieza de BD Tech Talks: «we have to assume adversaries can do the same, and likely will» (tenemos que asumir que los adversarios pueden hacer lo mismo, y probablemente lo harán). En aquel caso la vía de salida era el paquete publicado sin .npmignore, no el commit.

Por qué gitleaks y TruffleHog pasan por alto 215 de los ficheros

Apiiro pasó gitleaks y TruffleHog, con la verificación desactivada, sobre cada copia. De los 1.122 ficheros con secreto, gitleaks marcó 564 y TruffleHog 643, y 215 se les escaparon a los dos. La mayoría de los fallos son contraseñas dentro de un comando corriente, detrás de PGPASSWORD=, de sshpass -p o de mysql -p, que no tienen el formato de token que buscan las reglas por defecto. El dato tiene una lectura incómoda para quien confía en el escaneo de secretos del CI, y otra peor si se recuerda que el gusano Shai-Hulud de septiembre de 2025 usaba TruffleHog en las máquinas infectadas para recolectar secretos: los que no detecta el escáner tampoco los detecta el atacante que lo reutiliza, pero sí los encuentra quien lee el JSON a mano.

Para localizar el fichero y las reglas con pinta de credencial en todos los repositorios de una organización, incluido el historial, sirve la búsqueda del bloque siguiente, pensada para ejecutarse desde la carpeta que contiene los clones.

# Ficheros de Claude Code presentes hoy en cada clon
find . -path '*/.claude/settings.json' -o -path '*/.claude/settings.local.json' | sort

# Reglas allow que llevan una contraseña o un token dentro del comando
grep -rnE 'Bash\(.*(PGPASSWORD=|MYSQL_PWD=|sshpass -p|mysql .*-p[^ ]|--password[= ]|Authorization: Bearer |AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{20,}|sk-[A-Za-z0-9_-]{20,})' --include='settings*.json' .

# Commits que alguna vez tocaron el fichero, aunque hoy no exista
for r in */; do (cd "$r" && git log --all --oneline -- .claude/settings.local.json .claude/settings.json | sed "s|^|$r |"); done

Las expresiones cubren los patrones que Apiiro cita como más frecuentes y los prefijos habituales de AWS, GitHub y proveedores de modelos. La guía del blog sobre herramientas y recursos DevSecOps recoge dónde encajan gitleaks y TruffleHog en la cadena de integración continua.

Si la búsqueda devuelve algo, el orden importa y es el que piden Apiiro y la propia documentación de Anthropic.

  1. Rota hoy cada contraseña, token y clave que el fichero haya contenido alguna vez, antes de tocar el repositorio. Empieza por las emparejadas con un servidor público (SSH, bases de datos accesibles), porque dan acceso sin más pasos.
  2. Deja de versionar el fichero con git rm --cached .claude/settings.local.json y añade .claude/settings.local.json al .gitignore del repositorio, aunque ya figure en el fichero global de exclusiones. Si el fichero compartido .claude/settings.json tiene reglas con secretos, límpialas en un commit y rota también esas credenciales.
  3. Abre el settings.local.json de cada clon, aunque nunca se haya subido, y borra toda regla allow que contenga un secreto. El fichero sigue en el disco y cualquier infostealer lo encontrará.
  4. Cambia la forma de aprobar: comandos con variables ($DB_URL, $PGPASSWORD) cargadas desde un gestor de secretos, para que la regla guardada solo contenga el nombre de la variable, y aprobaciones por patrón (Bash(npm run:*)) en vez de comandos exactos.
  5. En el equipo de seguridad, trata .claude/, .cursor/, .gemini/ y .specstory/ y las transcripciones de agentes como un .env: añádelos a la plantilla de .gitignore de la organización, al pre-commit y al escaneo de historial del CI, y comprueba que el escáner detecta contraseñas dentro de comandos y no se limita a tokens con prefijo.
  6. Revisa dónde más viaja el repositorio (paquetes npm, imágenes Docker, archivos zip, carpetas sincronizadas). En España, si los secretos daban acceso a datos personales, notifica a INCIBE-CERT (empresas) o a CCN-CERT (sector público) y, llegado el caso, a la AEPD en 72 horas.

El punto cuatro es el único que ataca la causa, porque cualquier mecanismo que memorice un comando con la credencial escrita la memoriza con él, y ya contamos en la guía sobre cómo proteger las APIs que las claves de API viven mejor en un gestor de secretos que en la línea de comandos.

Un agente que escribe en el repositorio lo que el desarrollador teclea

La parte que distingue este caso de una fuga de .env clásica está en el commit. Que 539 de los 1.105 repositorios rastreados tengan el fichero subido con Claude como coautor significa que el agente aprobó un comando, lo guardó con su secreto y después, al hacer git add del proyecto, lo versionó él mismo. La exclusión global de git que añade Anthropic funciona en la máquina donde se escribió el fichero y en ninguna otra, y desde el blog ya avisamos, cuando Cursor sirvió una extensión de Claude Code con backdoor, de que el inventario de qué hace cada herramienta de desarrollo en cada puesto se ha convertido en trabajo del equipo de seguridad.

Gráfico de GitGuardian con los secretos por cada mil commits de Claude Code frente a desarrolladores humanos en 2025
Fuente: GitGuardian, «The State of Secrets Sprawl 2026»

Los números de GitGuardian ayudan a poner el hallazgo de Apiiro en escala. Su informe sobre la dispersión de secretos de 2026, publicado el 17 de marzo, contó 28,65 millones de secretos nuevos en commits públicos de GitHub durante 2025, un 34 % más que el año anterior, y midió que los commits con Claude Code como coautor filtraban secretos en el 3,2 % de los casos frente al 1,5 % del conjunto. El gráfico de arriba, tomado del informe, muestra cómo la tasa por cada mil commits del agente llegó a 31 en el verano de 2025 y bajó hasta 13 en diciembre, cerca de los 11 de los desarrolladores humanos.

Esa curva se midió antes de que Apiiro mirase dentro del fichero de permisos, y los 1.379 secretos de settings.local.json son configuración que el escáner medio no clasifica como sensible. En nuestra lectura, la cifra de GitGuardian y la de Apiiro miden dos fugas distintas con el mismo origen: un agente con acceso al shell y al repositorio hace lo que se le aprueba, y lo que se le aprueba a menudo lleva una credencial dentro. Esta misma mañana contábamos cómo Storm-3358 usa ARTEX para pasar de un secreto en GitHub a un Azure Key Vault en cinco minutos, y la semana pasada cómo los agentes de Cursor hicieron las intrusiones de Aur0ra. Los 501 secretos que abren una base de datos directamente son exactamente el tipo de entrada que esos agentes ofensivos buscan primero.

Lo que cabe esperar de Anthropic es un cambio en el prompt o en la regla guardada, por ejemplo ocultar los valores que parezcan credenciales antes de escribir la regla o añadir el fichero al .gitignore del repositorio en vez de al global, que es lo que pedía el issue de agosto de 2025. Hasta que llegue, el fichero seguirá guardando lo que el desarrollador aprueba, y el desarrollador seguirá aprobando lo que el agente propone.

Dudas habituales sobre Claude Code y sus permisos

¿Qué es Claude Code y cómo funciona?

Claude Code es el agente de programación de Anthropic para la terminal, VS Code, JetBrains y una app de escritorio. Lee el repositorio, edita ficheros y ejecuta comandos de shell en la máquina del desarrollador. Para cada comando que no está en su lista interna de solo lectura pide permiso, y las aprobaciones permanentes se guardan como reglas en los ficheros de ajustes.

¿Qué puede hacer Claude Code sin preguntar?

Depende de los ficheros de ajustes que lea. Las reglas allow de ~/.claude/settings.json valen para todos los proyectos del usuario, las de .claude/settings.json para todo el equipo que clone el repositorio y las de .claude/settings.local.json solo para ese usuario en ese proyecto. Una regla deny en cualquiera de ellos, por ejemplo Read(./.env), impide leer el fichero aunque otro ajuste lo permita, y es la forma documentada de dejar los secretos fuera del alcance del agente.

Temas

No hay comentarios todavía