Primero · ¿Qué es un token y por qué tanto cuidado?
Si acabas de empezar a programar con IA, probablemente escuchaste la palabra "token" o "API key" y todavía no te queda del todo claro qué es. Vamos por ahí.
Por eso importa guardarla bien. No es paranoia. Es responsabilidad básica.
Lo que pasa cuando una llave se filtra
Esto no es teoría. Son números de un reporte público de GitGuardian (empresa de ciberseguridad que escanea GitHub a diario):
secretos filtrados
en GitHub público durante 2023
crecimiento anual
se multiplican cada año
API keys de OpenAI
crecimiento en un solo año (2022→2023)
Y este es el dato que más asusta:
Caso real · Toyota, 5 años expuesto
En octubre de 2022, Toyota admitió públicamente que un token de acceso a su sistema T-Connect (su plataforma conectada de autos) había estado expuesto en GitHub durante 5 años.
Lo había subido por error un subcontratista en un repositorio público. Nadie revisó. Estuvo ahí 1.825 días.
duración de la fuga
entre 2017 y 2022
clientes afectados
emails + números de cliente expuestos
valor de un .env
como herramienta de seguridad
Si le pasa a Toyota, te puede pasar a ti. La diferencia es que tu llave de OpenAI no es defendida por un equipo de ciberseguridad. Es solo tuya. Si se filtra, tú pagas la cuenta.
Por eso esta guía existe.
La tesis
Tu Mac, Windows o Linux ya viene con una caja fuerte cifrada. Está ahí desde el día uno. Casi nadie la usa.
En cambio, mucha gente guarda sus llaves de OpenAI, Stripe o Anthropic en un archivo de texto plano llamado .env. Y un buen día se les filtra.
No vamos a hablar de "cuándo usar .env". Vamos a hablar de cómo dejar de usarlo.
1 · Las tres zonas donde puede vivir tu llave
2 · Por qué un .env es peligroso por defecto
Un archivo .env es texto plano. Sin cifrar. Cualquiera con acceso a tu disco lo lee.
Pero el riesgo real no es ese. El riesgo real es esta cadena:
3 · Mac · Tu Keychain ya está ahí
macOS viene con Keychain Access. Una caja fuerte cifrada con tu contraseña de usuario. La abre tu sistema cuando inicias sesión.
Guardar una llave
security add-generic-password \
-a "santaia" \
-s "santaia:openai-key" \
-w "sk-proj-tu-llave-real"
Leerla desde tu código
import subprocess
def get_secret(name: str) -> str:
return subprocess.run(
["security", "find-generic-password",
"-s", name, "-a", "santaia", "-w"],
capture_output=True, text=True, check=True
).stdout.strip()
openai_key = get_secret("santaia:openai-key")
Borrarla si la rotaste
security delete-generic-password -s "santaia:openai-key" -a "santaia"
4 · Windows · Credential Manager
Windows también lo tiene. Se llama Credential Manager y se maneja desde PowerShell con cmdkey.
Guardar una llave
cmdkey /generic:"santaia:openai-key" `
/user:"santaia" `
/pass:"sk-proj-tu-llave-real"
Leerla desde tu código
Necesitas el módulo CredentialManager (una sola vez):
Install-Module -Name CredentialManager -Scope CurrentUser
Después desde Python:
import subprocess
def get_secret(target: str) -> str:
ps = f'(Get-StoredCredential -Target "{target}").GetNetworkCredential().Password'
return subprocess.run(
["powershell", "-Command", ps],
capture_output=True, text=True, check=True
).stdout.strip()
openai_key = get_secret("santaia:openai-key")
5 · Linux · secret-tool
Linux usa libsecret como API estándar. Instala la CLI una vez:
# Debian / Ubuntu
sudo apt install libsecret-tools
# Fedora
sudo dnf install libsecret
Guardar una llave
secret-tool store --label="OpenAI · santaia" \
service "santaia" \
key "openai-key"
Te pregunta el valor de la llave en interactivo. No queda en tu historial de bash.
Leerla desde tu código
import subprocess
def get_secret(service: str, key: str) -> str:
return subprocess.run(
["secret-tool", "lookup", "service", service, "key", key],
capture_output=True, text=True, check=True
).stdout.strip()
openai_key = get_secret("santaia", "openai-key")
6 · Mi convención de naming
Cuando llevas 30+ llaves entre varios proyectos, sin convención no encuentras nada. Esta es la que uso:
patrón: servicio-contexto
gitlab-terracomercegitlab-personalanthropic-personal
Llaves que usas en varios proyectos. El sufijo dice para qué cuenta.
patrón: proyecto:servicio
santaia:openai-keysantaia:elevenlabs-keysantaia:gemini-key
Llaves específicas de un proyecto. Los dos puntos separan el proyecto del servicio. Fácil de filtrar:
security dump-keychain | grep santaia
7 · Producción · Nunca en tu máquina
Tu computador personal nunca debe tener llaves de producción. Punto.
Las llaves de producción viven en el panel del proveedor donde despliegas. Se inyectan solo cuando se ejecuta el build.
Cloudflare Pages
Ruta: Settings → Environment variables → Add
Pones nombre + valor + ambiente (Production / Preview). Guardas y redeploy. La variable solo existe durante el build.
Es lo que uso yo para santa-ia.com.
GitHub Actions
Ruta: Settings → Secrets and variables → Actions → New repository secret
En tu workflow lo lees con secrets.NOMBRE:
- env:
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
run: npm run deploy
Vercel
Ruta: Settings → Environment Variables
Misma idea que Cloudflare: nombre + valor + ambiente. Inyección automática durante el build.
Railway
Ruta: pestaña Variables de cada servicio
Puedes importar de un .env local en bloque. Esa es la única vez que un .env tiene sentido: importarlo a Railway una vez y nunca más.
8 · ¿Y si ya filtré algo?
Te puede pasar. Si pasó, no entres en pánico. Tres pasos en orden:
Rota la llave en el servicio
Esto primero. Mientras la llave vieja siga activa, el daño sigue creciendo.
- OpenAI: Dashboard → API Keys → Revoke → crea nueva
- Stripe: Dashboard → Developers → API keys → Roll
- AWS: IAM → Access Keys → Deactivate + Delete + Create new
Cualquier servicio serio tiene un botón de "rotate" o "revoke" en su panel.
Limpia el historial de git
Para repos privados y recientes, basta con:
git rm --cached .env
echo ".env" >> .gitignore
git commit -m "chore: remove .env from tracking"
git push
Para repos públicos o historiales largos, usa git-filter-repo:
pip install git-filter-repo
git filter-repo --invert-paths --path .env
git push --force
Anota qué pasó
En el README o tu sistema de notas: qué llave se filtró, qué hiciste, qué falló en tu proceso. Sin esto, el segundo incidente cae igual.
Cierre
Si pruebas esto en algún proyecto tuyo y se te traba algo, escríbeme en @santaia.lab. Las dudas reales son el material del próximo artículo.

