Fugas de secretos en settings.local.json de Claude Code: audita las reglas 'Don't Ask Again' y rota claves
Si eliges 'Yes, and don't ask again' en Claude Code, la herramienta guarda el comando aprobado tal cual lo escribiste en settings.local.json, dentro del proyecto. Si el comando contenía una credencial, el archivo ahora también la contiene. Investigadores de seguridad han hallado miles de estos secretos en repositorios públicos, imágenes de Docker y paquetes npm. Esta guía propone a los líderes de ingeniería la secuencia Encontrar, Neutralizar, Limpiar, Cerrar: localizar cada copia, rotar las credenciales antes de limpiar nada y cerrar las vías por las que se filtra el archivo.
GitGuardian encontró 5,343 secretos que seguían funcionando en el momento de su detección, todos en copias públicas de un único archivo de proyecto de Claude Code, .claude/settings.local.json. Las credenciales acaban ahí porque Claude Code pide permiso antes de ejecutar un comando de shell, y una de las respuestas que ofrece, "Yes, and don't ask again" (Sí, y no volver a preguntar), escribe el comando aprobado en ese archivo. GitGuardian y un segundo equipo de investigación, Apiiro, han publicado recuentos que muestran grandes cantidades de credenciales, desde contraseñas de bases de datos hasta tokens de API, en copias públicas de este único archivo.
Si eres responsable de ingeniería, líder de plataforma o de DevSecOps, o CTO con desarrolladores que usan Claude Code, esta guía te ofrece una secuencia práctica: encontrar cada copia expuesta, neutralizar las credenciales, limpiar los repositorios y artefactos y, por último, cerrar las vías por las que se escapa el archivo.
Qué escribe realmente en disco "Yes, and don't ask again"
Cuando un desarrollador elige "Yes, and don't ask again" para un comando Bash, ocurren dos cosas. Según GitGuardian, Claude Code ejecuta el comando y escribe una regla de permiso (allow rule) en .claude/settings.local.json, dentro de la carpeta del proyecto, y esa regla es la propia cadena del comando, clave de API incluida. Piensa en los comandos que los desarrolladores aprueban en un día normal de trabajo: una llamada PGPASSWORD=... psql a una base de datos de staging, un inicio de sesión sshpass -p '...' ssh, una sesión mysql -p... o un curl que lleva un bearer token. Si apruebas cualquiera de ellos de forma permanente, la credencial se guarda junto con el comando.
La documentación de Anthropic lo confirma. La documentación de configuración indica que elegir "Yes, and don't ask again" para un comando Bash guarda esa aprobación como regla de permiso en settings.local.json. La documentación de permisos añade que las aprobaciones permanentes se guardan en .claude/settings.local.json en la raíz del repositorio git, resuelta a través de los worktrees hasta el checkout principal.
Anthropic dispone de un informe público sobre este comportamiento desde agosto de 2025.
Magnitud de la exposición: dos recuentos de investigación, leídos en paralelo
Apiiro publicó su investigación el 6 de noviembre de 2026. Informó de que 1,122 de 104,185 copias públicas del archivo en GitHub contenían al menos un secreto. Esos archivos contenían 1,379 secretos distintos, entre ellos 379 contraseñas de bases de datos, 43 accesos SSH a servidores públicos y 23 claves de AWS. Cuando Apiiro amplió la búsqueda a versiones anteriores del archivo, al archivo de configuración compartido que los equipos suben a propósito y a transcripciones guardadas de chats con agentes, el total llegó a 2,539 secretos en 1,994 repositorios.
El análisis propio de GitGuardian encontró 31,088 secretos distintos en .claude/settings.local.json en GitHub público, repartidos en 23,276 repositorios, de los cuales 5,343 seguían funcionando al detectarse. Solo entre los secretos válidos de settings.local.json hay 1,955 cadenas de conexión de Postgres distintas, 292 contraseñas SSH y 941 claves de API de Google. Nuestra lectura de esta combinación es que gran parte del riesgo activo está en bases de datos y servidores, y que las cuentas de API de terceros son solo una parte del panorama.
| Dimensión | Apiiro | GitGuardian |
|---|---|---|
| Qué se analizó | Copias públicas de settings.local.json en GitHub; el recuento ampliado añade el historial del archivo, la configuración compartida y las transcripciones | settings.local.json en GitHub público, más un análisis independiente de Docker Hub y CLAUDE.md |
| Archivos o repositorios | 1,122 de 104,185 archivos contenían un secreto; 1,994 repositorios en el recuento ampliado | 23,276 repositorios |
| Secretos distintos | 1,379 (2,539 en el recuento ampliado) | 31,088 |
| Válidos al detectarse | No se indica en estas fuentes | 5,343 |
| Tipos de credenciales destacados | 379 contraseñas de bases de datos, 43 accesos SSH, 23 claves de AWS | 1,955 cadenas de conexión de Postgres, 292 contraseñas SSH, 941 claves de API de Google |
Los dos estudios difieren en alcance, periodo y método de detección, así que conviene leerlos como mediciones independientes de un mismo problema. Sumarlos supondría contar dos veces, y ninguno debe tomarse como corrección del otro.
GitGuardian también siguió la tendencia. Las fugas mensuales en settings.local.json alcanzaron un máximo de 12,535 casos en abril de 2026 y bajaron a 959 en octubre. GitGuardian afirma que todavía no sabe qué cambió.
Por qué la protección integrada y tus escáneres no lo detectan
Claude Code sí incluye una protección. Según la documentación de configuración, la primera vez que escribe el archivo en un repositorio git que todavía no lo ignora, añade **/.claude/settings.local.json al archivo global de exclusiones de git del usuario. Esa entrada vive en la máquina del propio desarrollador y queda fuera del .gitignore del proyecto. La nota de investigación de la Cloud Security Alliance explica por qué no basta: una exclusión global protege a un desarrollador en una estación de trabajo, y no viaja con el repositorio al clon de un compañero, a un runner de CI, a un portátil nuevo, a una imagen de contenedor ni a un tarball de npm.
La detección es la segunda brecha. En el conjunto de datos de Apiiro, gitleaks marcó 564 de los 1,122 archivos con fugas y TruffleHog marcó 643, pero ninguna de las dos herramientas marcó 215 de ellos, sobre todo porque los secretos eran contraseñas incrustadas en la sintaxis de comandos como PGPASSWORD=, sshpass -p o mysql -p. Las cifras de GitGuardian apuntan en la misma dirección. Los detectores genéricos (contraseñas, cadenas de alta entropía, JWT, bearer tokens, pares de usuario y contraseña) generaron el 63% de todos los casos en settings.local.json: 44,755 de 71,525.
Más allá de GitHub: imágenes de Docker, paquetes npm y CLAUDE.md
El archivo no se queda en git. GitGuardian examinó imágenes públicas de Docker Hub y encontró 613 secretos distintos en archivos de configuración de Claude Code, 99 de ellos válidos al detectarse, repartidos en 543 repositorios de imágenes y 7,625 etiquetas.
Esos recuentos de etiquetas muestran cómo se multiplica un único error. Un token de API de Cloudflare válido aparecía en 559 etiquetas de una sola imagen, y una contraseña de base de datos aparecía en 643 etiquetas de siete repositorios de imágenes. Según GitGuardian, borrar el archivo del repositorio no sirve de nada para las imágenes que ya se han publicado.
Los registros de paquetes conllevan el mismo riesgo. Una investigación de Check Point, citada en la nota de la CSA, encontró 428 paquetes npm públicos que incluían un archivo .claude/settings.local.json, y 33 de esos archivos, en 30 paquetes, contenían credenciales. La auditoría también debe ir más allá de los archivos de configuración. GitGuardian descubrió que los archivos CLAUDE.md contenían 6,155 secretos distintos, 2,537 de ellos válidos, la tasa de validez más alta de todos los archivos que examinó.
El playbook Encontrar, Neutralizar, Limpiar, Cerrar
El orden de los pasos importa más que cualquier comando concreto. Encontrar abarca los pasos 1 a 3, Neutralizar es el paso 4, Limpiar abarca los pasos 5 a 7 y Cerrar es el trabajo de prevención de la sección siguiente. Neutralizar va antes que Limpiar porque la limpieza no puede recuperar copias que ya existen en otros lugares: los forks y las imágenes ya publicadas conservan el secreto por mucho que reescribas el historial, y solo la rotación hace que esas copias no valgan nada.
Paso 1 (Encontrar): Inventario
Enumera todos los repositorios, registros de contenedores y paquetes publicados que controla tu organización, incluidos los repositorios archivados, los mirrors internos y los forks personales que la gente usa para trabajar. Lo que no esté en la lista no se limpiará.
Paso 2 (Encontrar): Busca en los árboles actuales y en todo el historial de git
Busca .claude/settings.local.json, .claude/settings.json, CLAUDE.md y cualquier transcripción exportada de agentes. Busca en todo el historial además de en la rama actual, porque el recuento mayor de Apiiro procedía de versiones antiguas de archivos, archivos de configuración compartidos y transcripciones. Una búsqueda filtrada por ruta en tu plataforma de código sirve como primera pasada rápida. Para una pasada por repositorio, sugerimos un comando como este, que también busca archivos .jsonl por si las transcripciones de tu equipo se guardaron en ese formato:
git log --all --name-only --pretty=format: | grep -E '[.]claude/|CLAUDE[.]md|[.]jsonl$' | sort -uToma esa salida como punto de partida para las transcripciones. Las que se hayan guardado fuera de git, o con otros nombres de archivo, requieren una búsqueda aparte.
Paso 3 (Encontrar): Lee los archivos, no solo la salida del escáner
Estos archivos son lo bastante cortos como para que alguien los abra y lea directamente las reglas de permiso. Para una búsqueda en todo el historial de repositorios grandes, sugerimos pasar las revisiones a git grep de una en una en lugar de todas a la vez:
git rev-list --all | while read -r rev; do
git grep -nE 'PGPASSWORD=|sshpass -p|mysql[^|]*[[:space:]]-p[^[:space:]]|Authorization: Bearer' "$rev" -- '*.json' '*.jsonl' '*.md'
doneEs posible que a algunos equipos un bucle así les resulte lento en historiales largos, y este tipo de búsquedas por patrones tiende a producir falsos positivos. Trata cada coincidencia como una credencial real hasta que una persona confirme que no lo es.
Paso 4 (Neutralizar): Rota o revoca primero cada credencial
Según GitHub, si los datos sensibles son un secreto, el primer paso es revocarlo o rotarlo. En la práctica, eso significa rotar la contraseña de la base de datos, volver a emitir el token y sustituir la credencial SSH, y después comprobar que cada valor antiguo ya no funciona. Hasta que hayas confirmado que una credencial está inutilizada, da por hecho que sigue activa.
Paso 5 (Limpiar): Deja de rastrear la configuración local y revisa la compartida
Deja de rastrear el archivo local y añade la regla de exclusión al propio .gitignore del proyecto, para que viaje a cada clon y a cada runner de CI:
git rm --cached .claude/settings.local.json
echo '.claude/settings.local.json' >> .gitignore
git add .gitignore
git commit -m "Stop tracking Claude Code local settings"También sugerimos pedir a cada desarrollador que elimine de su copia local del archivo las reglas que contengan credenciales.
Paso 6 (Limpiar): Reescribe el historial, reconstruye imágenes y vuelve a publicar paquetes
Una vez confirmada la rotación, reescribe el historial donde sea útil, reconstruye las imágenes afectadas y publica versiones limpias de los paquetes, asumiendo que quedará cierta exposición. GitHub advierte de que, si el commit que introdujo los datos sensibles existe en algún fork, seguirá siendo accesible allí. Sugerimos considerar que las versiones antiguas de los paquetes siguen siendo accesibles salvo que hayas confirmado lo contrario.
Paso 7 (Limpiar): Documenta lo que hiciste
Mantén un registro breve del incidente. Para cada credencial encontrada, anota el responsable, la fecha de rotación y cualquier exposición residual que no puedas eliminar, como forks, imágenes ya descargadas y versiones antiguas de paquetes. El registro convierte una limpieza puntual en algo que auditores y futuros equipos pueden verificar, y merece la pena incorporarlo a tu runbook de incidentes con agentes para que la próxima fuga siga un camino que la gente ya conoce.
Lista de verificación para entregar a los equipos
- [ ] Encontrar: repositorios, registros y paquetes enumerados, incluidos repos archivados y forks
- [ ] Encontrar: árboles e historial completo revisados en busca de archivos de configuración, CLAUDE.md y archivos .jsonl; transcripciones fuera de git buscadas por separado
- [ ] Encontrar: archivos leídos por una persona, más comprobaciones personalizadas para PGPASSWORD=, sshpass -p y mysql -p
- [ ] Neutralizar: cada credencial rotada o revocada, con el valor antiguo confirmado como inutilizado
- [ ] Limpiar: settings.local.json sin rastrear y en el .gitignore del proyecto; settings.json sin credenciales y revisado en las pull requests
- [ ] Limpiar: historial reescrito donde sea útil, imágenes reconstruidas, paquetes publicados de nuevo
- [ ] Limpiar: registro del incidente completado, con forks y versiones antiguas de paquetes anotados como exposición residual
- [ ] Cerrar: los controles de prevención siguientes implantados
Cerrar: mantener los secretos fuera de los comandos aprobados
La regla de permiso es la cadena literal del comando, así que la prevención se reduce a mantener las credenciales fuera de esa cadena.
- Usa variables en lugar de valores. Pasa las credenciales mediante variables de entorno o un gestor de secretos, para que el comando aprobado contenga el nombre de una variable y no el secreto. Define la variable fuera del comando aprobado, en la sesión de shell o en un wrapper del gestor de secretos, porque un
VAR=secret cmden línea sigue escribiendo el secreto en la regla de permiso. Antes: aprobarPGPASSWORD=s3cret psql -h staging-db -U appguarda la contraseña. Después: con PGPASSWORD ya definida en el entorno, aprobarpsql -h staging-db -U appno guarda ningún secreto. Para un patrón más robusto, consulta cómo un proxy de credenciales mantiene las claves de API fuera de los agentes de IA. - Lee antes de hacer clic. Antes de conceder una aprobación permanente, sugerimos leer el comando exacto y, si contiene una credencial, aprobarlo solo una vez. El mismo hábito de leer antes de aprobar se aplica también a las aprobaciones remotas de agentes.
- Sube la regla de exclusión al repositorio y revisa el archivo. Incluye .claude/settings.local.json en el .gitignore de cada proyecto en lugar de depender de exclusiones globales y, cada cierto tiempo, elimina las reglas que los desarrolladores ya no necesitan.
- Bloquéalo en pre-commit y en CI. Rechaza los commits que añadan el archivo de configuración local y marca la sintaxis de comandos con credenciales que los escáneres estándar pueden pasar por alto.
- Cierra las vías de empaquetado. Añade las rutas .claude/ a .dockerignore y usa una lista explícita de archivos permitidos o una lista de exclusión para los paquetes npm, de modo que el archivo no pueda colarse.
Lo que ha dicho Anthropic hasta ahora
El informe de error original tiene un historial irregular. La nota de la CSA recoge que se marcó como "not planned" (no planificado) el 3 de enero de 2026, sin que la página muestre respuesta de ningún mantenedor. El informe se presentó en agosto de 2025, y las fuentes revisadas aquí no explican el intervalo entre esa presentación y la fecha de cierre de enero.
Desde entonces, Anthropic ha respondido públicamente, aunque no ha publicado una corrección. El responsable de Claude Code, Boris Cherny, declaró a iTnews: "Históricamente, Claude Code no ha hecho limpieza de secretos (hay muchas herramientas buenas para eso), pero estamos estudiando qué podemos hacer para evitarlo." En Hacker News, Cherny enumeró posibles enfoques, entre ellos asegurarse de que el modo automático detecte esto e integrar un escaneo básico de secretos. Dijo que el equipo crearía prototipos de algunos de ellos para entender las ventajas e inconvenientes antes de lanzar algo.
Salvedades y límites de esta guía
- La validez es una instantánea. Los 5,343 secretos válidos de GitGuardian reflejan el momento de la detección. Algunos pueden haberse rotado desde entonces, y otros quizá no.
- El descenso no tiene explicación. GitGuardian no sabe por qué bajaron los casos mensuales después de abril de 2026, así que la caída no demuestra que el problema esté resuelto.
- Los repositorios privados quedan fuera del alcance. La investigación abarca repositorios, imágenes y paquetes públicos. Los repositorios privados tienen la misma exposición allí donde los archivos se comparten o se integran en artefactos.
- El comportamiento puede cambiar. Vuelve a consultar la documentación oficial de configuración y permisos de Claude Code antes de cerrar tu política interna.
- Los comandos son puntos de partida. Los fragmentos de búsqueda anteriores son sugerencias que debes adaptar y probar en tu propio entorno, no herramientas de detección completas.
Puntos clave
- 1'Yes, and don't ask again' guarda el comando completo, credenciales incluidas, en .claude/settings.local.json.
- 2Trabaja en orden: Encontrar cada copia, Neutralizar las credenciales, Limpiar repos y artefactos y, después, Cerrar las vías de escape.
- 3Rota o revoca primero. Los forks y las imágenes ya publicadas conservan copias que la limpieza no puede alcanzar.
- 4Pon la regla de exclusión en el .gitignore de cada proyecto. La exclusión global integrada solo protege una máquina.
- 5Los escáneres estándar no detectan contraseñas incrustadas en comandos, así que añade comprobaciones personalizadas y haz que una persona lea los archivos.
- 6Audita más allá de git: la configuración compartida, CLAUDE.md, las transcripciones, las imágenes de Docker y los paquetes npm entran en el alcance.
- 7Define las credenciales fuera de los comandos aprobados. Un VAR=secret en línea sigue acabando en la regla de permiso.
Conclusión
La decisión que tiene delante un líder tecnológico es si debe tratar como expuesta cada credencial que haya pasado alguna vez por una aprobación permanente. Teniendo en cuenta la investigación resumida arriba y las carencias que revela tanto en los escáneres como en la exclusión global integrada, sugerimos que la opción más segura por defecto es que sí. Eso implica reservar ahora tiempo de ingeniería para la rotación y una auditoría completa, antes de que ningún escáner lance una alerta. Anthropic ha dicho que está trabajando en la prevención, pero hasta que se publique y documente una corrección, planifica como si el comportamiento no fuera a cambiar.
También sugerimos que alguien ajeno al proyecto original revise las reglas de permiso de Claude Code de tus equipos, junto con las comprobaciones de pre-commit y CI destinadas a detectar credenciales ocultas en la sintaxis de los comandos.
Preguntas frecuentes
¿Elegir 'Yes, and don't ask again' en Claude Code guarda de verdad mi contraseña o clave de API en texto plano?
Sí, si la contraseña o la clave formaba parte del comando. Claude Code guarda la cadena del comando aprobado como regla de permiso en .claude/settings.local.json, y la documentación de configuración de Anthropic confirma que las aprobaciones permanentes de Bash se almacenan ahí. Si el comando llevaba una credencial, esa credencial queda ahora en el archivo JSON tal cual la escribiste. Para que el valor real no acabe en la regla guardada, define los secretos como variables de entorno fuera del comando aprobado.
¿Git ignora settings.local.json automáticamente, y por qué no es suficiente?
En parte. La primera vez que Claude Code escribe el archivo en un repositorio que todavía no lo ignora, añade **/.claude/settings.local.json al archivo global de exclusiones de git del desarrollador. Eso cubre a un desarrollador en una estación de trabajo. La entrada no viaja al clon de un compañero, a un runner de CI, a un portátil nuevo, a una imagen de contenedor ni a un tarball de npm, así que añade también la regla al propio .gitignore de cada proyecto.
¿Debo rotar el secreto o reescribir primero el historial de git?
Rota primero. La guía de GitHub indica que, cuando los datos sensibles son un secreto, el primer paso es revocarlo o rotarlo. La reescritura del historial viene después y no puede llegar a todo: si el commit existe en algún fork, sigue siendo accesible allí. También sugerimos considerar que las versiones antiguas de los paquetes siguen siendo accesibles. Confirma que la credencial antigua ya no funciona antes de empezar la limpieza.
¿Encontrarán gitleaks o TruffleHog estas fugas?
Algunas. En el conjunto de datos de Apiiro, gitleaks marcó 564 de 1,122 archivos con fugas y TruffleHog marcó 643, pero ninguno de los dos marcó 215 archivos. La mayoría eran contraseñas incrustadas en la sintaxis de comandos como PGPASSWORD=, sshpass -p o mysql -p. GitGuardian descubrió que los detectores genéricos generaron 44,755 de 71,525 casos. Añade comprobaciones personalizadas y haz que alguien lea los archivos directamente.
¿Qué archivos y artefactos, además de settings.local.json, debo auditar?
Incluye el archivo compartido .claude/settings.json, CLAUDE.md, las transcripciones exportadas de agentes, las imágenes de contenedor y los paquetes publicados. GitGuardian encontró 613 secretos distintos en archivos de configuración de Claude Code en imágenes públicas de Docker Hub y 6,155 secretos distintos en archivos CLAUDE.md. Según una investigación de Check Point, recogida por SecurityBrief y citada por la Cloud Security Alliance, 428 paquetes npm públicos incluían un archivo .claude/settings.local.json, y 33 de esos archivos, en 30 paquetes, contenían credenciales.
¿Ha corregido Anthropic el problema?
No, según las fuentes revisadas aquí. El responsable de Claude Code, Boris Cherny, dijo que históricamente la herramienta no ha hecho limpieza de secretos y que el equipo está estudiando cómo prevenirlo. Ideas como la detección en el modo automático y un escaneo básico de secretos integrado se probarán mediante prototipos antes de lanzar nada.
Fuentes
- https://apiiro.com/blog/coding-agents-leaking-secrets-github-dont-ask-again
- https://blog.gitguardian.com/claude-code-settings-local-json-secrets-docker-hub/
- https://code.claude.com/docs/en/settings
- https://code.claude.com/docs/en/permissions
- https://thehackernews.com/2026/11/claude-codes-dont-ask-again-file.html
- https://www.csoonline.com/article/4247336/claude-codes-dont-ask-again-button-has-a-secrets-spilling-problem.html
- https://labs.cloudsecurityalliance.org/research/csa-research-note-claude-code-settings-secret-leak-20261108/
- https://github.com/anthropics/claude-code/issues/5544
- https://news.ycombinator.com/item?id=50408176
- https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository
- https://www.itnews.com.au/news/dont-ask-again-in-anthropics-claude-code-leaks-developer-secrets-630203
Escrito por
Hamza DiazHamza Diaz es el fundador de Optijara, donde crea agentes de IA prácticos, sistemas de automatización y flujos de trabajo de Copilot para empresas de servicios. Escribe sobre operaciones de IA, estrategia de agentes e implementación real para equipos que quieren sistemas útiles en lugar de promesas vacías.
