Seguridad de claves API en Claude Code: guía de higiene de tokens
Evita que Claude Code lea y filtre tus claves de API: gestiona los secretos en desarrollo con 1Password CLI y op run usando plantillas que nunca tocan el disco.
Hay un espectro de seguridad en tu entorno de producción como usuario de IA. Escribes en una ventana de chat que, por cómo está construida, ve todo lo que pegas, lo registra y (en la mayoría de productos) lo conserva durante al menos 30 días para revisión de «seguridad» y probablemente entrenamiento. Incluido lo que no querías pegar: el token de Cloudflare en el archivo dev.vars, la clave de OpenAI en el script, la cadena de conexión a la base de datos con la contraseña incrustada.
Así es como gestiono las claves de producción en mi máquina de desarrollo Windows, con Claude Code, sin inventar una capa personalizada de gestión de secretos. Es, por así decirlo, la versión de arranque de un sistema de gestión de claves de producción más empresarial que esperarías en un equipo de desarrollo serio.
Aunque existen configuraciones mucho más rigurosas para equipos que mantienen servicios de producción serios (GitHub deploy secrets, Cloud Run secrets manager, Vault, AWS KMS), el artículo de hoy no va para esa gente. Va para el desarrollador en solitario o la consultora pequeña cuyo asistente de IA tiene acceso a demasiado, por accidente, porque los secretos están en dotfiles en claro en un repositorio local legible.
Por qué esto importa muchísimo
En abril de este año, Jesse Davis, un desarrollador de Sídney de Agentic Labs, se despertó con una factura de Google Cloud de 25.672,86 AUD (aproximadamente 18.391,78 USD). Su clave de API había acabado donde no debía, alguien la encontró y consumió años de uso normal en un fin de semana. Google acabó abonando el gasto, pero solo tras un tenso intercambio y mucha exposición pública. (Tom's Hardware cubrió la historia.)
La historia de Davies no es tan inusual. Solo se está haciendo más visible a medida que más gente recurre a Claude Code para sus propios proyectos de desarrollo. El informe State of Secrets Sprawl 2026 de GitGuardian contó 28,6 millones de credenciales filtradas en repositorios públicos de GitHub en 2025, y 1,27 millones de esas eran claves relacionadas con la IA en concreto (OpenAI, Anthropic, Hugging Face y demás). Las fugas de claves de IA crecieron más rápido que cualquier otra categoría de secretos el año pasado. (Informe completo de GitGuardian.)
La mayoría de esas claves se filtraron de la misma forma: alguien tenía un archivo .env con claves reales, alguien tenía un asistente de IA con un radio de lectura amplio y el asistente, o bien commitó el archivo por accidente, o bien referenció su contenido de forma que acabó en un log.
Hay un artículo de Medium dando vueltas titulado We Told Claude Code to Deny Access to Our Secrets. It Read Them Anyway. El resumen (tl;dr): una regla de denegación en .claude/settings.json para Read(./*.env) no impidió al agente hacer grep del contenido del archivo y meterlo en el resultado de una llamada a una herramienta. La regla de denegación funcionaba a nivel literal de lectura de archivo, pero el agente tenía otras formas de ver el texto del archivo y usó una de ellas.
No confíes en el buen comportamiento del agente para mantener los secretos a salvo. No pongas los secretos donde el agente pueda verlos en primer lugar.
Ese es el principio sobre el que se construye el artículo de hoy y usa mi procedimiento como ejemplo.
Para qué sirve esta guía
Para:
- Una configuración funcional para mantener los tokens de API fuera de tu ventana de chat de IA en una máquina de desarrollo Windows o macOS.
- Las herramientas concretas con las que lo probé: 1Password CLI,
op run, un script de Node para verificación y mi integración deastro dev.
No es:
- Una configuración para equipos con varios desarrolladores. Si tienes un equipo con acceso compartido a producción, quieres GitHub OIDC + gestores de secretos en la nube, no 1Password. Estoy familiarizado con Cloud Run secrets manager y GitHub secrets (que uso para despliegue en algunos proyectos).
- Una guía de higiene de secretos empresarial. GitHub deploy secrets, Cloud Run secrets manager, AWS Secrets Manager, Doppler, Infisical, Vault están más adelante en esta curva.
- Un sustituto de una disciplina correcta de rotación de claves. Esta configuración facilita la rotación; no la hace por ti. Renuevo mis claves de forma habitual durante las fases de despliegue de un proyecto y, sobre todo, como precaución si me tomo un fin de semana largo o unos días en otro proyecto.
Por ahora, el listón es: cuando ejecuto pnpm dev en un repo, los secretos están presentes en el env del worker, pero nunca en ningún archivo de mi disco del que un agente de IA pueda hacer grep hacia una llamada a una herramienta.
Observarás que este flujo concreto usa mi entorno de Cloudflare como base; si usas otra plataforma, también vale: lo que cuenta es la disciplina.
Esto es lo que hay que hacer:
Paso 0. Regístrate en 1Password
Elegí 1Password porque lo había usado antes y la CLI funciona muy bien para esto. Bitwarden tiene una CLI equivalente y los principios de este artículo sirven para ambos.
Ve a 1password.com y regístrate. El plan que quieres es Individual (3,99 £/mes en el momento de escribir esto). 1Password lo renombró hace poco desde «Personal», así que las guías antiguas que muestran un botón «Personal» están desfasadas. Haz clic en Individual.
Cuando completas el registro, 1Password te emite una Secret Key. No es lo mismo que tu contraseña maestra. Es el componente criptográfico que descifra tu caja fuerte en un dispositivo nuevo, y sin ella tu contraseña maestra por sí sola no te dará acceso. 1Password ofrece un PDF de Emergency Kit que reúne la Secret Key con el email de tu cuenta; la práctica recomendada es imprimir el PDF y guardar la impresión en una caja fuerte física.
Queda avisado:
No pongas el Emergency Kit en otro gestor de contraseñas. Eso crea una dependencia circular con la contraseña maestra que estás intentando respaldar.
Guarda el archivo o almacénalo como cualquier otra clave criptográfica para todo tu mundo: con cuidado.
Paso 1. Instala la app de escritorio
Cuando tengas una cuenta, descarga la app desde 1password.com/downloads. En el primer arranque, la app pide el email de tu cuenta, la contraseña maestra y la Secret Key. Necesitarás la Secret Key del PDF del Emergency Kit.
Una vez dentro, verás tu caja fuerte Personal por defecto.
Paso 2. Instala la CLI
En Windows:
winget install AgileBits.1Password.CLI
Es un comando único y sencillo, no hay descarga manual y queda fijado a la versión que 1Password distribuye a través de la Microsoft Store. En macOS el equivalente es brew install --cask 1password-cli.
Cierra y vuelve a abrir el terminal tras la instalación. winget añade la CLI de 1Password a tu PATH, pero el cambio no se propaga a las ventanas de terminal que ya estaban abiertas. Si
op --versiondevuelve «command not found» en la misma ventana donde ejecutaste la instalación, es por eso.
Si estás en un equipo corporativo con winget bloqueado, el instalador manual funciona bien; solo son unos clics más.
Paso 3. Activa la integración de la CLI en la app de escritorio
Abre la app de escritorio de 1Password, ve a Settings (el engranaje, o Ctrl+,) y elige Developer en la barra lateral. Activa «Integrate with 1Password CLI». Luego cierra la app por completo desde la bandeja del sistema y vuelve a abrirla.
La integración solo se activa con un arranque nuevo de la app, no al guardar la configuración.
El panel Developer tiene otros interruptores. «Use the SSH agent» sirve para usar 1Password como tu almacén de claves SSH, lo cual es un flujo distinto, así que déjalo desactivado por ahora.
Paso 4. Primera autenticación con la CLI
Abre un terminal nuevo (para que se cargue el env tras la integración). Ejecuta:
op vault list
La primera vez que se ejecuta deberías ver un aviso de Windows Hello (o Touch ID en macOS).
Autentícate con lo que ya tengas configurado para la capa biométrica del sistema. Los comandos siguientes dentro de la misma ventana de desbloqueo se ejecutarán sin que tengas que volver a autenticarte.
Si ves una tabla de cajas fuertes, has terminado la instalación.
Paso 5. Crea una caja fuerte dedicada para desarrollo
op vault create converly-dev
(Sustituye converly-dev por el nombre que quieras. Yo nombro las mías por el proyecto, para saber de un vistazo qué credenciales contiene esa caja fuerte.)
Yo acotaría esta app solo para desarrollo, en lugar de dejar que consuma también tus credenciales personales; por si metes a otro desarrollador en el equipo.
Paso 6. Añade tu primer secreto
Aquí es donde entra la disciplina: vas a añadir una credencial de API pulsando «add new» (el botón azul en la esquina superior derecha de la app de 1Password). Esto es lo que hay que hacer:
- Abre la app de escritorio. Haz clic en tu caja fuerte dedicada. Haz clic en «+ New Item» y elige API Credential.
- Title: el nombre exacto de la variable de entorno. Por ejemplo:
CLOUDFLARE_API_TOKEN. El título importa porque la sintaxis de referencia (op://vault/title/credential) se resuelve contra él. - Credential: el valor del secreto. Pégalo aquí, en la app de escritorio, no vía
op item createdesde la línea de comandos. - Username: déjalo en blanco para los tokens Bearer. El campo Username es un vestigio de la antigua plantilla Login-item de 1Password, donde cada credencial tenía una identidad de «usuario». Los tokens de API modernos no la tienen. (Para credenciales de dos partes como AWS access key + secret, entonces sí pones el access key ID en Username.)
- Type: lo dejo en blanco si tengo dudas. Las opciones del desplegable (Bearer, API Key, Basic, OAuth) son metadatos para tu yo del futuro, no algo que lea
op run.
Guarda. Ejecuta op item list --vault=converly-dev para confirmar que ha llegado.
No rellenes la caja fuerte por adelantado. Añade secretos cuando tengas un script que necesite consumirlos. Una caja fuerte atiborrada de entradas especulativas es más difícil de auditar, aumenta la probabilidad de apuntar la clave equivocada al endpoint equivocado y da una falsa sensación de completitud. También es una pérdida de tiempo.
Paso 7. Conéctalo a tu proyecto
Tocar claves de API es un rollo, pero el trabajo compensa.
Crea en tu repo un archivo llamado .dev.vars.template. Dentro, pon solo referencias a los títulos de API, sin añadir material secreto:
CLOUDFLARE_API_TOKEN=op://converly-dev/CLOUDFLARE_API_TOKEN/credential
EMDASH_AUTH_SECRET=op://converly-dev/EMDASH_AUTH_SECRET/credential
YUBHUB_API_KEY=op://converly-dev/YUBHUB_API_KEY/credential
BREVO_NEWSLETTER_LIST_ID=2
CONTACT_FROM_EMAIL=hola@converly.es
Este archivo es seguro para hacer commit. No contiene secretos, solo punteros a tu caja fuerte local de 1Password. Cualquiera que lea el repo puede ver qué credenciales necesita el proyecto sin ver ninguna de las tuyas.
(Si antes tenías un archivo .dev.vars con valores reales, bórralo ahora. Es el archivo que el agente ha podido leer con grep todo este tiempo. Gitignore no debería ser tu única defensa aquí.)
Luego, actualiza package.json:
"scripts": {
"dev": "op run --env-file=.dev.vars.template -- astro dev",
"dev:no-vault": "astro dev"
}
dev:no-vault es una vía de escape para cuando 1Password no está disponible o necesitas saltarte la inyección. Lo mantengo porque ahí está cuando lo quiero, pero me alegra informar de que aún no he tenido que recurrir a él.
Ejecuta pnpm dev. Si todo está bien conectado verás un aviso de Windows Hello y luego un arranque normal del dev server. Los secretos están presentes en process.env durante la vida del proceso astro dev y luego desaparecen cuando el proceso termina. Nunca tocan el sistema de archivos.
En mi caso, la prueba que confirmó un test end-to-end con éxito fue ver cómo /jobs hacía un server-side-fetch a la API de YubHub usando mi YUBHUB_API_KEY. La cadena completa llegó al worker, los secretos estaban donde debían y no se commitó nada.
Paso 8. Los distintos ámbitos necesitan plantillas distintas
Una vez que la inyección de dev funciona, querrás ejecutar scripts que apuntan a producción: copias de seguridad, publicaciones de contenido y smoke tests contra tu entorno de producción. Esos scripts necesitan secretos distintos a los del dev server, claro.
La tentación, a la que cedí unos cinco minutos antes de aprender lo contrario, es añadir overrides en línea a op run:
# This does not work.
op run --env-file=.dev.vars.template --env CLOUDFLARE_API_TOKEN=op://... -- node script.mjs
op run no tiene un flag --env. Asumí que sí, por analogía con Docker y kubectl, que sí lo tienen. El op run de 1Password solo acepta rutas --env-file. Otras herramientas de CLI de secretos (Doppler, Infisical) sí soportan --env en línea, lo que en parte explica por qué el hueco confunde.
La respuesta portable es una plantilla por ámbito. Crea un segundo archivo:
# .prod-ops.vars.template - references for production-targeting scripts
CLOUDFLARE_API_TOKEN=op://converly-dev/CLOUDFLARE_API_TOKEN/credential
EMDASH_API_KEY=op://converly-dev/EMDASH_API_KEY_PROD/credential
Y añade scripts por propósito que elijan la plantilla correcta:
"scripts": {
"dev": "op run --env-file=.dev.vars.template -- astro dev",
"backup": "op run --env-file=.prod-ops.vars.template -- node scripts/backup-production.mjs",
"verify:prod": "op run --env-file=.prod-ops.vars.template -- node scripts/verify-prod-access.mjs"
}
El naming importa aquí más de lo que parece. --env-file=.prod-ops.vars.template se lee, de un vistazo, como este comando se ejecuta contra producción. No tienes que escarbar en el cuerpo del script para ver el radio de impacto.
Tu yo del futuro, dentro de seis semanas, echa un vistazo al package.json y sabe al instante qué scripts mueven estado real de producción.
La disciplina conversacional (la parte de la que nadie escribe)
Aquí está la parte que he ido deduciendo a base de prueba y error.
Las herramientas de arriba mantienen los secretos fuera de tu sistema de archivos. No impiden que tú, el humano, los teclees a mano en la ventana de chat. Lee esta disciplina tomada de ops de producción:
El script es el contrato. Lo que imprime es, por construcción, seguro para pegar. Cualquier cosa que el script no imprima no está en la salida para empezar.
Cuando ejecutas pnpm verify:prod, el script imprime una salida de estado que, por diseño, no es secreta. Imprime OK · 200 · 12 buckets, no imprime el token. Esa salida es segura para hacer con ella lo que quieras. Cuando el asistente de IA pregunta «¿cuál ha sido el resultado?», la respuesta es lo que imprimió el script.
Si la respuesta requiriera pegar un valor que el script no imprimió, la respuesta es «escribiré un script nuevo que imprima lo que necesitamos ver y luego pegaré esa salida». Nunca el valor en crudo.
Por eso el script pnpm verify:prod de este artículo existe en la forma en la que existe. No es un script de depuración que escribo de nuevo cada vez. Es un contrato. Siempre imprime la misma forma de salida (código de estado, recuento, confirmación del token solo en longitud). Los tokens contra los que prueba se cargan desde 1Password, se usan para hacer llamadas a la API y luego se recolectan como basura. Nunca aparecen en pantalla. Nunca aparecen en el chat, nunca se envían a Anthropic con la esperanza de que esto esté bien.
La misma disciplina se aplica un nivel por encima:
- Pegar «la salida de
pnpm dev» está bien. El dev server imprime listados de rutas y logs de peticiones, no valores de entorno. - Pegar «el contenido de mi archivo
.dev.vars» no está bien, pero además: no debería haber un archivo.dev.vars. Hay un.dev.vars.template, que son solo referencias. - Pegar «el resultado de
op item get CLOUDFLARE_API_TOKEN» no está nada bien. Ese comando devuelve el secreto. No hay versión de esto en la que debas ejecutarlo dentro de una sesión de chat.
Las herramientas hacen fácil lo correcto. La disciplina hace imposible hacer lo incorrecto por accidente.
Qué sigue faltando en esta configuración
Mi configuración me lleva a «sin secretos reales en dotfiles, el agente de IA no tiene nada que leer con grep, el pegado en la conversación pasa por scripts». Cosas que no hace:
Reglas de denegación en .claude/settings.json.
El artículo de Medium que cité muestra que las reglas de denegación Read(./*.env) no funcionan del todo en Claude Code a finales de 2025. Aun así vale la pena activarlas porque cazan los errores fáciles; no sustituyen que el agente no vea el archivo en absoluto. Debo decir que he notado que Claude últimamente es más cauteloso con el tema de las claves de API y puede que esté respetando el settings.json, pero «puede» no es exactamente una certeza.
Escaneo de secretos en pre-commit.
Gitleaks como hook de pre-commit cazará el caso en el que tú (o tu asistente de IA) escribís un archivo con lo que parece un token e intentáis hacer commit. Unas líneas de configuración de pre-commit te salvan de los modos de fuga más bochornosos.
Disciplina de rotación.
Esta configuración facilita la rotación (op item edit actualiza el valor, cada script lo recoge en la siguiente ejecución) pero no la impone. Algunos tokens tienen costes de rotación en cascada (rotar un auth secret de EmDash invalida todas las passkeys WebAuthn almacenadas; te quedarás fuera de tu propio admin si no tienes documentado antes un procedimiento de recuperación por magic-link). Este es un riesgo, por ejemplo, con emdash, el propio cms que estás viendo ahora.
Configuraciones para equipos.
El acceso para varios desarrolladores quiere las cajas fuertes compartidas de 1Password y, más importante, el movimiento a nivel de organización desde «los desarrolladores tienen credenciales de producción en sus portátiles» a «los desarrolladores no tienen credenciales de producción en absoluto, empujan código a través de un pipeline de CI con credenciales emitidas por OIDC». Eso es otra cosa y mucho más relevante para equipos grandes de desarrolladores.
El cierre de un minuto
Si lees esto y no haces nada más: borra tu .dev.vars, tu .env, lo que sea donde hayas estado guardando tokens reales. El agente ha tenido acceso de lectura a ese archivo durante todo el tiempo que ha existido en tu repo. Aunque configures 1Password mañana, cada minuto que ese archivo existe es otro minuto en el que podría estar en la respuesta de alguna llamada a una herramienta. Acostúmbrate a rotar los tokens con la frecuencia suficiente y acabarás hartándote y aprendiendo a hacerlo mejor.
No seas Jesse Davies, abriendo la bandeja de entrada a una factura de 25.672 A$. No seas el desarrollador de las estadísticas de GitGuardian. La configuración es un coste único solo en tiempo; filtrar tus credenciales de API en internet no lo es.
Artículos relacionados
3 trucos para optimizar tokens en Claude Code (Fable 5)
Optimiza tokens y ventana de contexto en Claude Code tras Fable 5: /compact, desglose de contexto y conectores activos. Ahorro acumulativo para uso intensivo diario.
Qué es harness engineering: el arnés que hace útil al modelo
Qué es harness engineering: mejorar el arnés del LLM (contexto, tools, hooks, verificación) sin cambiar el modelo. Caso LangChain en Terminal Bench 2.0.
Requisitos de Claude Code: Mac, Windows y Linux (2026)
Requisitos de sistema de Claude Code en Mac, Windows y Linux en 2026: RAM, disco, Node, rutas de instalación nativas, autenticación y los fallos más comunes al instalar el CLI.

