Cómo limpiar credenciales expuestas en Git con git-filter-repo y rotar tokens
Etiquetas: git,seguridad,devops,credencialesEl problema real
Hace poco me di cuenta de que había pusheado un archivo .env con credenciales de API a un repositorio privado. Aunque fuera privado, no es excusa. Un acceso comprometido, un repositorio que se vuelve público, o simplemente una auditoría de seguridad hubiera expuesto mis tokens. Aprendí que no puedo confiar en eliminar archivos en commits posteriores—Git mantiene todo el historial.
Por qué git-filter-repo
Hace años hubiera usado git filter-branch, pero es lento y propenso a errores. git-filter-repo es la herramienta moderna recomendada por los mantenedores de Git. Es rápido, preciso y tiene mejores opciones para este trabajo.
Instalación
En mi servidor Debian:
apt-get install git-filter-repo
O con pip:
pip3 install git-filter-repo
Paso 1: Identificar los daños
Primero, necesito saber qué commits contienen credenciales. Busco patrones sospechosos:
git log --all --oneline | head -20
git log -p --all | grep -i "token\|password\|api_key" | head -10
También reviso qué archivos sensibles están en el historial:
git log --all --full-history -- ".env"
git log --all --full-history -- "config.yml"
En mi caso, encontré que .env se había commitido en 3 ocasiones y un archivo credentials.json en 2.
Paso 2: Hacer backup
Nunca hago esto sin backup:
cd /path/to/my/repo
git clone --mirror . backup-mirror.git
Si algo sale mal, tengo una copia completa del repositorio con todo su historial.
Paso 3: Limpiar archivos específicos
Ejecuto git-filter-repo para eliminar los archivos sensibles del historial completo:
git-filter-repo --invert-paths --path .env --path credentials.json
El parámetro --invert-paths significa que mantiene todo EXCEPTO lo que especifico. Esto es lo contrario a lo que parece, pero funciona perfectamente.
El proceso tarda algunos segundos y reescribe todo el historial. Al final, veo:
Processed 47 commits
New history has 47 commits
Paso 4: Forzar el push (con cuidado)
Como he reescrito el historial, necesito hacer un push forzado. En un servidor doméstico donde soy el único dev, es seguro:
git push origin --force --all
git push origin --force --tags
Si es un repositorio compartido, coordino con el equipo para que todos hagan git reset --hard origin/main después.
Paso 5: Rotar credenciales comprometidas
Las credenciales que estaban en Git ya no están, pero debo asumir que fueron comprometidas. Roto todos los tokens:
- API Keys: Revoco las antiguas en el panel de la API y genero nuevas
- Contraseñas: Cambio la contraseña de cualquier servicio que usaba esas credenciales
- Tokens de BD: Regenero credenciales de bases de datos
- SSH Keys: Si estaban expuestas, genero nuevas parejas
Documento estos cambios en un archivo privado (no en Git):
2026-05-19 - Rotación de credenciales post-exposición
- API key antigua: revocada, nueva generada
- Token DB: regenerado
- Contraseña servicio X: cambiada
Paso 6: Prevención futura
Agrego reglas a .gitignore (ahora está limpio):
.env
.env.local
credentials.json
secrets/
También configuro un hook pre-commit para detectar patrones peligrosos:
git config core.hooksPath .githooks
Y creo .githooks/pre-commit:
#!/bin/bash
if git diff --cached | grep -iE "(password|token|api_key|secret)" && \
! git diff --cached | grep ".gitignore"; then
echo "⚠️ Posible credencial detectada. Abortando."
exit 1
fi
Conclusión
La limpieza toma 10 minutos. La rotación de credenciales, otro rato. Pero es tiempo bien invertido. En un servidor doméstico, no tengo excusa para ser negligente con secretos. Ahora uso variables de entorno y archivos locales que nunca entran a Git.
Lección aprendida: Los secretos nunca van en control de versiones, ni siquiera «privado». Punto.
«`
Equipamiento recomendado
- YubiKey 5 NFC — Llave de seguridad física para SSH y GitLab — elimina el riesgo de tokens robados
- Disco duro externo 2TB — Backup de repositorios antes de operaciones destructivas como git-filter-repo
Enlaces de afiliado. Sin coste extra para ti.