Security
9 min read
5129 views

Fantasmas en la Máquina: Cómo eliminar permanentemente secretos de tu historial de Git 👻

IT
InstaTunnel Team
Published by our engineering team
Fantasmas en la Máquina: Cómo eliminar permanentemente secretos de tu historial de Git 👻

Cada desarrollador enfrenta su peor pesadilla: estás revisando tu último commit cuando notas algo—una clave API, una contraseña de base de datos o un token de acceso de AWS que te mira desde tu código. Tu corazón se hunde. Inmediatamente haces un nuevo commit eliminando el secreto, respiras aliviado y sigues adelante. Pero aquí está la verdad aterradora: ese secreto todavía está allí, acechando en tu historial de Git como un fantasma en la máquina.

Entender por qué simplemente eliminar un secreto en un nuevo commit no es suficiente—y saber cómo exorcizar verdaderamente estos fantasmas digitales—es conocimiento crítico para cualquier desarrollador que trabaje con sistemas de control de versiones.

Por qué eliminar no es suficiente: Entendiendo la historia inmutable de Git

Git no fue diseñado pensando en el olvido. Su arquitectura fundamental está construida para preservar cada cambio, cada commit y cada versión de archivo durante toda la vida del repositorio. Este enfoque hace que Git sea excelente para rastrear cambios y recuperar trabajo perdido, pero se convierte en una grave vulnerabilidad de seguridad cuando datos sensibles entran en el repositorio.

Cuando haces un commit de un archivo que contiene un secreto, Git almacena una instantánea completa de ese archivo en su base de datos de objetos. Incluso si en el siguiente commit eliminas el secreto, el commit anterior—junto con su instantánea que contiene el secreto—permanece accesible permanentemente en la historia del repositorio.

Cualquier persona con acceso a tu repositorio puede viajar en el tiempo usando comandos como git log, git checkout o git show para ver el estado exacto de cualquier archivo en cualquier punto de la historia. Si tu repositorio es público o ha sido clonado por varios desarrolladores, ese secreto potencialmente ha sido distribuido a docenas o cientos de lugares.

La situación se vuelve aún más crítica cuando consideras que el número de secretos hard-coded detectados aumentó en un 67% de 2021 a 2022, con 10 millones de nuevos secretos encontrados solo en commits públicos en GitHub. Estas estadísticas subrayan la magnitud del problema y por qué la eliminación adecuada de secretos es esencial.

El alcance del problema: qué pasa después de la exposición

Una vez que un secreto entra en tu historia de Git, varios escenarios problemáticos pueden desarrollarse:

Recolección automatizada de secretos: actores maliciosos usan herramientas automatizadas para escanear continuamente repositorios públicos en busca de credenciales expuestas. Estos bots pueden detectar y explotar secretos en minutos después de su exposición. GitHub y otras plataformas han respondido implementando capacidades de escaneo de secretos, con el escaneo de secretos en GitHub protegiendo a los usuarios buscando tipos conocidos de secretos como tokens y claves privadas.

Forks y clones del repositorio: si tu repositorio es público o ha sido bifurcado, el secreto existe en múltiples ubicaciones fuera de tu control. Incluso si reescribes la historia de tu repositorio, todos los clones y forks existentes retendrán los datos comprometidos.

Logs de pipelines CI/CD: los secretos en tu repositorio podrían estar siendo registrados durante procesos automatizados de construcción, creando vectores de exposición adicionales que van más allá del repositorio mismo.

Sistemas de respaldo: las copias de seguridad y archivos del repositorio capturan tu historia de Git en puntos específicos en el tiempo, potencialmente preservando secretos indefinidamente incluso después de limpiar tu repositorio principal.

Comprender estos riesgos resalta por qué es necesario actuar de inmediato y de manera exhaustiva cuando se comete un error al incluir secretos.

Antes de comenzar: pasos críticos de preparación

Antes de intentar purgar secretos de tu historia de Git, debes completar varios pasos esenciales de preparación:

1. Rota el secreto comprometido inmediatamente

Tu primera acción debe ser invalidar el secreto expuesto. Genera nuevas credenciales, revoca tokens de API o cambia contraseñas. Este paso debe hacerse antes de reescribir la historia, porque el secreto ha sido potencialmente comprometido en el momento en que entró en tu repositorio.

2. Haz una copia de seguridad de tu repositorio

Reescribir historia es una operación destructiva. Crea una copia completa de tu repositorio antes de proceder:

git clone --mirror https://tu-repositorio-url.git respaldo-repo.git

Este clon espejo preserva todas las ramas, etiquetas y referencias, permitiéndote recuperar si algo sale mal durante la limpieza.

3. Coordina con tu equipo

Si varios desarrolladores trabajan en el repositorio, comunica claramente tus planes. Reescribir historia requerirá que todos vuelvan a clonar el repositorio o rebaseen cuidadosamente sus ramas locales. Programa la limpieza en un período de baja actividad si es posible.

4. Documenta todas las ramas afectadas

Identifica todas las ramas que puedan contener el secreto comprometido:

git log --all --full-history --oneline -- path/to/file/with/secret

Este comando muestra cada commit en todas las ramas que modificaron el archivo que contiene tu secreto.

Selección de herramientas: git-filter-repo vs BFG Repo-Cleaner

Dos herramientas principales dominan el panorama para reescribir la historia de Git: git-filter-repo y BFG Repo-Cleaner. Cada una tiene fortalezas distintas y casos de uso ideales.

git-filter-repo: La potencia flexible

git-filter-repo es el reemplazo moderno, recomendado oficialmente, para el obsoleto comando git-filter-branch. Ofrece una flexibilidad sin igual para reescrituras complejas de repositorios.

Ventajas: - Capacidades de filtrado extremadamente flexibles - Puede realizar filtrados sofisticados basados en rutas - Maneja escenarios complejos como dividir repositorios o combinar múltiples repos - Mantenido activamente con actualizaciones regulares - Mejor rendimiento que git-filter-branch en la mayoría de operaciones

Ideal para: - Escenarios complejos que requieren control preciso - Repositorios donde los secretos aparecen en rutas o patrones específicos - Situaciones que requieren múltiples tipos de filtrado simultáneamente - Equipos cómodos con herramientas basadas en Python

BFG Repo-Cleaner: El especialista en velocidad

BFG Repo-Cleaner se describe como una alternativa más sencilla y rápida a git-filter-branch para limpiar datos no deseados en la historia de Git, capaz de eliminar contraseñas, credenciales y otros datos privados.

Ventajas: - Significativamente más rápido que git-filter-branch en operaciones simples - Interfaz de línea de comandos más sencilla para tareas comunes - Escrito en Scala, funciona en cualquier sistema con Java instalado - Excelente para eliminación sencilla de secretos

Ideal para: - Eliminar cadenas de texto específicas en todos los archivos a través de la historia - Limpiezas directas y rápidas - Equipos que quieren resultados rápidos con mínima configuración - Repositorios donde los secretos aparecen como cadenas de texto simples

Método 1: Usando BFG Repo-Cleaner para eliminación rápida de secretos

BFG Repo-Cleaner es excelente para eliminar patrones de texto específicos en toda la historia del repositorio. Aquí una guía paso a paso completa.

Instalación

BFG requiere Java 8 o superior. Descarga la última versión desde el repositorio oficial:

# Descarga BFG (verifica la última versión)
wget https://repo1.maven.org/maven2/com/madgag/bfg/1.14.0/bfg-1.14.0.jar

# Crea un alias para facilitar su uso
alias bfg='java -jar /ruta/a/bfg-1.14.0.jar'

Eliminación de secretos paso a paso

Paso 1: Clona un espejo limpio

Crea un clon espejo sin cabeza de tu repositorio:

git clone --mirror https://tu-repositorio-url.git repositorio-temporal.git
cd repositorio-temporal.git

El flag --mirror asegura que obtienes todas las referencias, ramas y etiquetas para una limpieza completa.

Paso 2: Crea un archivo de secretos

Crea un archivo de texto listando todos los secretos que necesitas eliminar. Cada secreto en una línea:

sk_live_51AbCdEfGhIjKlMnOp
AKIAIOSFODNN7EXAMPLE
db_password_prod_2024
google_api_key_12345

Guarda este archivo como secrets.txt fuera del directorio del repositorio.

Paso 3: Ejecuta BFG

Ejecuta BFG para reemplazar todas las ocurrencias de estos secretos:

bfg --replace-text secrets.txt repositorio-temporal.git

BFG escaneará toda la historia del repositorio y reemplazará cada ocurrencia de los secretos listados con ***REMOVED*** por defecto. Puedes personalizar el texto de reemplazo si lo deseas.

Paso 4: Limpia el repositorio

BFG actualiza tus commits y todas las ramas y etiquetas para que estén limpios, pero no elimina físicamente los datos no deseados. Debes usar garbage collection de Git para completar la eliminación:

cd repositorio-temporal.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive

Estos comandos expiran todas las entradas de reflog y hacen una recolección de basura agresiva para eliminar físicamente los objetos que contienen secretos del sistema de objetos de Git.

Paso 5: Verifica y empuja

Verifica que los secretos hayan sido eliminados revisando una copia de trabajo:

cd ..
git clone repositorio-temporal.git verificacion-repo
cd verificacion-repo
git log -p --all | grep -i "tu-patrón-secreto"

Si la verificación confirma la eliminación exitosa, fuerza el push a tu repositorio remoto:

cd repositorio-temporal.git
git push --force --all
git push --force --tags

Método 2: Usando git-filter-repo para eliminación precisa

git-filter-repo ofrece un control más granular cuando necesitas filtrado sofisticado más allá de reemplazos simples de texto.

Instalación

Instala git-filter-repo usando pip o tu gestor de paquetes:

# Usando pip
pip install git-filter-repo

# O en Ubuntu/Debian
apt-get install git-filter-repo

# O en macOS
brew install git-filter-repo

Paso a paso: filtrado basado en rutas

Paso 1: Clona tu repositorio

Crea una clonación fresca (no un repositorio sin cabeza esta vez):

git clone https://tu-repositorio-url.git limpieza-repo
cd limpieza-repo

Paso 2: Elimina archivos que contienen secretos

Si los secretos están en archivos específicos que quieres eliminar por completo:

git filter-repo --path config/secrets.yaml --invert-paths

El flag --invert-paths elimina la ruta especificada en todos los commits a lo largo de la historia.

Paso 3: Elimina secretos de archivos específicos

Para eliminar contenido dentro de archivos en lugar de archivos completos, usa la opción --replace-text:

echo "sk_live_51AbCdEfGhIjKlMnOp==***REMOVED***"  replacements.txt
git filter-repo --replace-text replacements.txt

Paso 4: Filtrado basado en rutas para casos complejos

Puedes combinar múltiples operaciones de filtrado. Por ejemplo, eliminar secretos solo de un directorio específico:

git filter-repo --path src/legacy/ --replace-text secrets.txt

Paso 5: Verifica y empuja

Después del filtrado, verifica el estado de tu repositorio:

git log --all --oneline --graph
git log -p | grep -i "patrón-secreto"

Agrega tu remoto (git-filter-repo elimina remotos por seguridad):

git remote add origin https://tu-repositorio-url.git
git push --force --all
git push --force --tags

Después de la limpieza: acciones de seguimiento esenciales

Reescribir la historia con éxito es solo el comienzo. Varios pasos críticos de seguimiento aseguran una remediación completa:

1. Actualiza a todos los miembros del equipo

Envía instrucciones claras a todos los miembros:

IMPORTANTE: La historia del repositorio ha sido reescrita para eliminar datos sensibles.

Acciones requeridas:
1. Elimina tu clon local
2. Clona de nuevo desde: [url-del-repositorio]
3. NO intentes fusionar o rebasear ramas existentes

Si tienes trabajo sin subir, guarda tus cambios como parches primero:
git format-patch origin/main

2. Actualiza las solicitudes de extracción abiertas

Cualquier solicitud de extracción abierta basada en la historia antigua debe ser recreada. Los commits antiguos ya no son compatibles con la historia reescrita.

3. Revisa forks y espejos

Si tu repositorio ha sido bifurcado o espejado, contacta a los propietarios de esos repositorios. Explica el problema de seguridad y solicita que actualicen sus copias o las eliminen si están desactualizadas.

4. Revisa logs y artefactos de CI/CD

Verifica los logs y artefactos de tu sistema de integración continua en busca de cualquier instancia del secreto expuesto. Estos sistemas a menudo almacenan en caché logs de construcción que pueden contener información sensible.

5. Monitorea uso no autorizado

Incluso después de rotar, monitorea tus sistemas en busca de uso no autorizado de las credenciales antiguas. Configura alertas para patrones de acceso sospechosos.

Prevención: Nunca vuelva a suceder

La mejor forma de manejar secretos en Git es prevenir que lleguen allí en primer lugar:

1. Usa hooks de Git para escaneo previo al commit

Implementa hooks de pre-commit que escaneen en busca de posibles secretos:

# .git/hooks/pre-commit
#!/bin/bash
if git diff --cached | grep -iE "password|secret|api[_-]?key|token"; then
    echo "⚠️  ¡Se detectó un secreto potencial! Commit bloqueado."
    exit 1
fi

2. Aprovecha herramientas de escaneo de secretos

Las herramientas modernas pueden detectar credenciales filtradas antes de que sean empujadas. Estas herramientas buscan en código, configuraciones y infraestructura patrones, entropía y a veces aprendizaje automático.

3. Usa variables de entorno y sistemas de gestión de secretos

Almacena secretos en variables de entorno o sistemas dedicados:

  • Desarrollo local: Usa archivos .env (y añádelos a .gitignore)
  • Producción: Usa AWS Secrets Manager, HashiCorp Vault o Azure Key Vault
  • CI/CD: Usa el almacenamiento de secretos de tu plataforma (GitHub Secrets, variables de GitLab CI/CD)

4. Mantén un .gitignore completo

Crea y mantiene un .gitignore exhaustivo:

# Variables de entorno
.env
.env.local
.env.*.local

# Configuración de IDE
.vscode/
.idea/

# Archivos de configuración con secretos
config/secrets.yml
config/database.yml
credentials.json

# Credenciales del proveedor cloud
.aws/
.gcloud/

5. Implementa revisiones de código obligatorias

Establece revisiones de código obligatorias antes de fusionar a las ramas principales. Capacita a tu equipo para detectar secretos accidentalmente comprometidos durante las revisiones.

6. Activa protección de push

La detección de secretos con protección de push puede detectar automáticamente secretos que coincidan con patrones específicos y evitar que sean empujados a los repositorios. Activa estas funciones en plataformas que las soporten.

Conclusión: Vigilancia eterna

Eliminar secretos de la historia de Git es una operación compleja y de alto riesgo que requiere ejecución cuidadosa y seguimiento exhaustivo. Aunque herramientas como BFG Repo-Cleaner y git-filter-repo hacen que el proceso técnico sea manejable, la coordinación, verificación y prevención son igualmente importantes.

Recuerda estos principios clave:

  1. Actúa de inmediato: Rota las credenciales comprometidas antes de limpiar la historia
  2. Elige la herramienta adecuada: Usa BFG para velocidad y simplicidad, git-filter-repo para escenarios complejos
  3. Comunica claramente: Asegúrate de que todos los miembros entiendan el proceso y sus acciones requeridas
  4. Verifica a fondo: No confíes en la limpieza hasta que hayas verificado que los secretos realmente desaparecieron
  5. Previene recurrencias: Implementa medidas preventivas integrales para evitar repetir este doloroso proceso

Los fantasmas en tu historia de Git pueden ser invisibles, pero son amenazas muy reales para tu postura de seguridad. Con el conocimiento y las herramientas correctas, puedes exorcizarlos por completo y establecer prácticas que mantengan tus secretos seguros desde el principio. Mantente vigilante, mantente seguro, y recuerda: en el mundo del control de versiones, lo que entra no sale fácilmente—a menos que sepas cómo hacerlo realidad.

Continue from this article into the most relevant product guides and workflows.

Related Topics

#git secrets removal, remove secrets from git history, delete sensitive data git, git-filter-repo, BFG Repo-Cleaner, purge git history, remove credentials from git, git security, exposed API keys git, remove passwords from git, committed secrets fix, accidentally committed password, git history rewrite, how to remove secrets from git history permanently, leaked credentials git, API key in git history, git secret scanning, remove API keys from git repository, delete committed passwords git, clean git repository, fix git security issue, git sensitive data removal, rewrite git commits, exposed secrets github, security breach git, compromised credentials repository, git filter repo tutorial, BFG repo cleaner guide, permanently delete files from git history, remove credentials from all git branches, git secret remediation, credential rotation, repository cleanup, git force push, prevent secrets in git commits, version control security, git security best practices, GitHub secret scanning, secret detection tools, automated secret scanning, repository security audit, git filter-branch alternative, DevSecOps, CI/CD security, push protection, pre-commit hooks, secret management, git reflog, git garbage collection, GitHub secrets, clean compromised repository, remove leaked API keys, secure git repository, GitLab security, developer security, DevOps security practices, git object database, git history cleanup tools, Bitbucket repository cleaning, repository management, code security tutorial, software engineer guide, git advanced techniques, protect source code, environment variables best practices, gitignore sensitive files, git repository forensics, source code security, security incident response, eliminate security vulnerabilities git

Keep building with InstaTunnel

Read the docs for implementation details or compare plans before you ship.

Share this article

More InstaTunnel Insights

Discover more tutorials, tips, and updates to help you build better with localhost tunneling.

Browse All Articles