Gestión de secretos con Claude Code: de .env a la rotación en producción
Guía para proteger .env, secretos CI/CD, claves cloud, logs redactados y límites seguros en Claude Code.
Las API keys, las URL de base de datos, los OAuth client secrets y las credenciales de despliegue en la nube son necesarios para cualquier aplicación real. También son los valores que, si se copian sin cuidado, acaban viviendo en el historial de Git, logs de CI, capturas, tickets, README y borradores de artículos. Cuando usas Claude Code para depurar, migrar, desplegar o documentar, la regla inicial debe ser clara: Claude Code puede ayudarte a construir el sistema seguro, pero no debe ver, imprimir, guardar ni reutilizar secretos reales.
Gestionar secretos no es solo meter contraseñas en una caja fuerte. Es separar configuración y código, aislar local, desarrollo, staging y producción, aplicar mínimos privilegios, validar la configuración al arrancar, redactar logs y tener un proceso de rotación antes de que haya un incidente. El principio de configuración de The Twelve-Factor App sigue siendo una buena base: la configuración debe venir del entorno, no estar hardcodeada en el repositorio. En la práctica necesitas también .gitignore, .env.example, un loader con validación, CI/CD secrets, un secret store cloud y una checklist de rotación.
flowchart LR
Dev["Local .env"] --> Loader["Validated config loader"]
CI["CI/CD secrets"] --> Deploy["Deployment runtime"]
Store["AWS / GCP / Azure secret store"] --> Deploy
Deploy --> App["Application process"]
App --> Logs["Redacted logs"]
1. Decide qué va en cada lugar
El error de principiante es tratar todas las variables de entorno igual. Una prueba práctica: si al filtrarse un valor alguien puede gastar dinero, enviar email, leer datos, escribir datos, desplegar código o suplantar a un usuario, trátalo como secreto. Una URL pública, el nombre de una feature flag o un OAuth client ID suelen tener menor sensibilidad porque por sí solos no conceden permisos.
| Caso de uso | Ejemplos | Desarrollo local | Producción |
|---|---|---|---|
| Email e integraciones API | SendGrid API key, Stripe secret key, GitHub token | Usa claves sandbox o test en .env | Inyecta desde CI/CD secrets o secret store |
| Base de datos | DATABASE_URL, usuario, contraseña | Usuario local o solo dev | Separa lectura/escritura y rota contraseñas |
| Despliegue cloud | Credenciales AWS/GCP/Azure | Evita claves locales de larga vida | Usa OIDC o rol solo de despliegue |
| OAuth y webhooks | OAUTH_CLIENT_SECRET, webhook signing secret | App local separada | Secretos prod fuera del repo |
La separación por entorno es lo importante. No reutilices la key de producción de Stripe en local. No pongas en CI un GitHub token personal con acceso amplio a la organización. No des permisos de administrador a una credencial que solo despliega. Eso es mínimo privilegio: el alcance justo, en el entorno correcto y, si es posible, con duración limitada.
2. Committea la forma, nunca el valor
.env es cómodo para trabajar en local, pero no debe entrar en Git. El repositorio debe contener .env.example, con nombres de variables, formato, origen y propósito. Para onboarding, combínalo con la guía de gestión de variables de entorno y reducirás preguntas repetidas.
# Local secrets
.env
.env.*
!.env.example
!.env.test.example
# Logs and generated output that may contain tokens
npm-debug.log*
yarn-debug.log*
coverage/
dist/
# .env.example - placeholders only, never real values
NODE_ENV=development
APP_BASE_URL=http://localhost:3000
# Use a local database user, not production credentials.
DATABASE_URL=postgres://app_user:replace-me@localhost:5432/app_dev
# Use test/sandbox keys for local development.
SENDGRID_API_KEY=SG.xxxxxx
STRIPE_SECRET_KEY=sk_test_xxxxxx
GITHUB_TOKEN=ghp_xxxxxx
# OAuth secrets must be separated by environment.
OAUTH_CLIENT_ID=local-client-id
OAUTH_CLIENT_SECRET=replace-me
# Deployment reads these from CI or a cloud secret store.
AWS_REGION=ap-northeast-1
DEPLOY_ROLE_ARN=arn:aws:iam::123456789012:role/app-deploy-dev
Cuando pidas ayuda a Claude Code, define el límite: puede leer .env.example, manifests de despliegue, scripts de package y nombres de variables; no puede abrir .env, copiar valores reales al chat ni escribir credenciales en docs. Si una implementación necesita un valor real, lo configuras tú en local, CI o el secret store, y Claude Code solo recibe el nombre de la variable.
3. Valida al arrancar y redacta los logs
Una app debe fallar rápido si falta un secreto o si tiene formato inválido. También debe dificultar que alguien imprima un token durante una sesión de debug. Este loader de Node.js usa dotenv y envalid para validar el entorno y ofrece redactConfig para logs, health checks y soporte.
import { config as loadDotenv } from "dotenv";
import { cleanEnv, str, url } from "envalid";
const envFile = process.env.NODE_ENV === "test" ? ".env.test" : ".env";
loadDotenv({ path: envFile });
const secretKeyPattern = /(KEY|TOKEN|SECRET|PASSWORD|DATABASE_URL|PRIVATE)/i;
const env = cleanEnv(process.env, {
NODE_ENV: str({
choices: ["development", "test", "staging", "production"],
default: "development",
}),
APP_BASE_URL: url({ default: "http://localhost:3000" }),
DATABASE_URL: url(),
SENDGRID_API_KEY: str(),
STRIPE_SECRET_KEY: str(),
GITHUB_TOKEN: str(),
OAUTH_CLIENT_ID: str(),
OAUTH_CLIENT_SECRET: str(),
AWS_REGION: str({ default: "ap-northeast-1" }),
DEPLOY_ROLE_ARN: str(),
});
export function appConfig() {
return Object.freeze({
nodeEnv: env.NODE_ENV,
appBaseUrl: env.APP_BASE_URL,
databaseUrl: env.DATABASE_URL,
sendgridApiKey: env.SENDGRID_API_KEY,
stripeSecretKey: env.STRIPE_SECRET_KEY,
githubToken: env.GITHUB_TOKEN,
oauthClientId: env.OAUTH_CLIENT_ID,
oauthClientSecret: env.OAUTH_CLIENT_SECRET,
awsRegion: env.AWS_REGION,
deployRoleArn: env.DEPLOY_ROLE_ARN,
});
}
export function redactValue(key, value) {
if (!secretKeyPattern.test(key)) return value;
if (!value) return "<empty>";
const text = String(value);
if (text.length <= 8) return "<redacted>";
return `${text.slice(0, 4)}...${text.slice(-4)}`;
}
export function redactConfig(config) {
return Object.fromEntries(
Object.entries(config).map(([key, value]) => [key, redactValue(key, value)]),
);
}
if (process.argv[1] === new URL(import.meta.url).pathname) {
console.log(redactConfig(appConfig()));
}
Un fallo típico es añadir console.log(process.env) en una incidencia nocturna y pegar el log de CI en un ticket o en Slack. Si luego pides a Claude Code que resuma esos logs crudos, puede volver a insertar secretos en tests, documentación o un artículo. Primero redacta, después pregunta. Las capturas también deben recortarse o difuminarse antes de subirlas.
4. Dale límites explícitos a Claude Code
Claude Code casi nunca necesita el valor real. Necesita nombres, formatos, errores ya redactados y la finalidad de cada permiso. Copia este bloque al inicio de tareas de seguridad, despliegue o depuración.
You may inspect .env.example, package.json, deployment manifests, and secret names.
Do not open, print, summarize, store, or copy .env, CI/CD secret values, cloud credentials, production dumps, or screenshots containing tokens.
When you need a value, ask me to set it outside chat and confirm only the variable name.
If a secret appears in command output, stop, redact it, and report which file or command exposed it.
Before changing permissions, explain the least-privilege scope and ask for approval.
Do not paste real secrets into prompts, logs, documentation, code comments, tests, tickets, or articles.
También limita comandos. printenv, cat .env, CLI cloud que muestran credenciales, dumps completos de logs de CI y capturas deben requerir confirmación. Con .env.example, tipos, errores redactados y documentación oficial, Claude Code puede resolver la mayoría de cambios sin tocar valores reales.
5. Usa CI/CD secrets y secret stores con intención
Una separación práctica es .env para local, CI/CD secrets para pipelines y un secret store cloud para runtime de producción. En AWS usa AWS Secrets Manager. En Google Cloud, Secret Manager. En Azure, Key Vault. En GitHub activa secret scanning para detectar tokens filtrados cuanto antes.
name: deploy
on:
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
env:
NODE_ENV: production
AWS_REGION: ap-northeast-1
steps:
- uses: actions/checkout@v4
- name: Validate required secret names
run: test -n "${{ secrets.DEPLOY_ROLE_ARN }}" && test -n "${{ secrets.DATABASE_URL }}"
- name: Deploy without echoing secrets
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
DEPLOY_ROLE_ARN: ${{ secrets.DEPLOY_ROLE_ARN }}
run: npm run deploy
La trampa de CI es “hagamos echo para ver qué pasa”. GitHub Actions enmascara muchos secretos exactos, pero no siempre protege valores derivados, URLs codificadas, JSON, capturas o valores que no se registraron como secret. Pide a Claude Code nombres de variables y pasos de validación, no valores. Si propone permisos amplios, exige una razón y reduce el alcance.
6. Rota antes de que sea obligatorio
La rotación debe ser mantenimiento normal, no solo respuesta a incidentes. SendGrid, Stripe, GitHub tokens, contraseñas de base de datos, OAuth client secrets, webhook signing secrets y políticas de confianza cloud necesitan owner, entorno, consumidores, fecha de última rotación y próxima revisión.
## Secret rotation checklist
- [ ] Identify owner, environment, consumers, and business impact.
- [ ] Create a new secret with the smallest required scope.
- [ ] Store it in CI/CD secrets or the cloud secret store.
- [ ] Deploy one service or job with the new value.
- [ ] Confirm logs and metrics without printing the secret.
- [ ] Revoke the old secret.
- [ ] Scan Git history, tickets, docs, screenshots, and chat snippets.
- [ ] Record the rotation date and next review date.
Claude Code es útil para convertir esto en un issue template, runbook o plan de migración. No le pases el secreto antiguo ni el nuevo. Pídele algo como: “Encuentra todas las referencias a SENDGRID_API_KEY y prepara un plan seguro para cambiar a la key v2”.
7. Revisa los fallos antes de publicar
Detén la publicación o el despliegue si ocurre algo de esto:
.envo.env.productionfue committeado y la key expuesta no se revocó.- Capturas, logs de CI, reportes de error o ejemplos de artículos contienen tokens, URLs de DB u OAuth secrets.
- Una clave cloud tiene permisos de administrador cuando bastaría con desplegar.
- Credenciales de producción de Stripe, SendGrid, base de datos o GitHub se usan en local.
- OAuth client secrets o webhook signing secrets no tienen owner ni runbook de rotación.
- Un asistente de IA recibió valores reales y luego los reutilizó en README, tests, tickets o borradores.
Los incidentes de seguridad suelen venir de huecos de proceso. Usa la checklist de auditoría de seguridad, revisa la guía de fallos de seguridad, conecta esto con la guía de desarrollo API y, si usas SendGrid o correo transaccional, consulta el artículo de automatización de email.
8. Implantación realista en equipo
No empieces migrando todos los servicios a un secret store en una semana. Elige una app y completa el camino: .gitignore, .env.example, loader validado, logs redactados, CI/CD secrets, secret store de producción y registro de rotación. Después migra los tipos recurrentes: SendGrid, Stripe, GitHub token, DATABASE_URL, OAuth client secret y credenciales de despliegue.
En las formaciones y consultorías de ClaudeCodeLab trabajamos con la forma real de tu repositorio, pero sin recibir secretos reales. Definimos qué puede inspeccionar Claude Code, diseñamos límites de CI/CD secrets, activamos GitHub secret scanning, planificamos AWS/GCP/Azure secret stores y dejamos prompts y runbooks reutilizables. Así el equipo puede aplicarlo en code review al día siguiente.
En resumen: gestionar secretos no es solo esconder cadenas. Es separar valores del código, aislar entornos, validar al arrancar, redactar logs, reducir permisos y practicar la rotación. Claude Code puede construir y revisar ese sistema, pero no debe convertirse en otro lugar donde se copian secretos.
Al probar este flujo en la app Node.js de ejemplo de Masa, el loader detectó valores faltantes, redactConfig evitó que la URL de base de datos y las API keys aparecieran en CI, y el workflow de deploy ya no necesitó permisos amplios de escritura. La sorpresa fue una captura antigua con una Stripe test key visible, así que se reemitió antes de publicar. La lección práctica: revisa logs, imágenes y borradores con la misma sospecha que el código fuente.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Escalera de permisos de Claude Code para ampliar acceso sin perder control
Pasa de read-only a ediciones limitadas, comandos de prueba y checks de deploy con menos riesgo.
Claude Code Small PR Proof Pack: cambios pequeños que sí se pueden revisar
Un paquete de prueba para PRs de Claude Code: diff, checks, URL pública, CTA y rollback.
Gate de revisión antes del commit con Claude Code
Cómo revisar con Claude Code antes del commit: diff, build, URL pública, Gumroad, consultoría, tests y archivos ajenos.