Oleksii Siniaiev
RU UK ES EN
Navegación de la página del artículo

Artículo del blog · Artículos

Cuando un secreto filtrado en Git acabó reescribiendo cuatro años de historial

Una historia real de claves API filtradas, un git filter-branch en pánico que reescribió 4 años de historial, y la respuesta correcta que debimos haber seguido.

Publicado: April 11, 2026 Actualizado: April 11, 2026 10 min de lectura
Enviar un mensaje Ver proyectos
Flowchart showing the correct incident response steps when secrets are leaked in Git

Si subiste secretos a Git por accidente, no empieces reescribiendo el historial. Empieza rotando o revocando las credenciales expuestas, elimina el archivo con un commit normal y añade una barrera para que el error no se repita. Ese orden reduce el riesgo real sin romper el repositorio del resto del equipo.

Una vez, en un trabajo anterior, presencié una situación que se me quedó grabada. Un desarrollador experimentado subió accidentalmente un archivo config.json con claves API activas a un repositorio compartido. Después vino una cadena de decisiones bien intencionadas que convirtió un incidente manejable en un problema mucho mayor.

En esta guía explico qué ocurrió, qué aprendió el equipo y qué deberías hacer si commiteaste por error una API key, un token, una contraseña u otro secreto en GitHub, GitLab, Bitbucket o cualquier remoto Git compartido.

Respuesta rápida: qué hacer si commiteaste secretos en Git

  1. Rota o revoca el secreto inmediatamente. Es el único paso que reduce el riesgo de seguridad real.
  2. Elimina el archivo o el secreto en un commit nuevo. No hagas un git push --force por pánico en una rama compartida.
  3. Añade el archivo o patrón a .gitignore. Así reduces la probabilidad de repetir el incidente.
  4. Audita el alcance de la exposición. Revisa logs, permisos y sistemas relacionados.
  5. Reescribe el historial solo si hay una razón real. Si hace falta, hazlo con coordinación y control.

Cómo empezó: credenciales en un archivo de configuración

Fue un commit rutinario. El desarrollador estaba trabajando en la integración de un servicio y no se dio cuenta de que el nuevo archivo config.json incluía claves API de producción. El archivo fue añadido al staging, commiteado y pusheado al repositorio remoto compartido. Nadie lo detectó en la revisión de código. Las claves quedaron en el historial de commits, visibles para cualquiera con acceso al repositorio.

El problema se descubrió al día siguiente durante una revisión normal. Alguien abrió el archivo y vio credenciales de producción en texto plano dentro del control de versiones.

La reacción de pánico: «limpiemos el historial»

El primer impulso fue eliminar el archivo y asegurarse de que no pudiera encontrarse tampoco en el historial. Una búsqueda rápida llevó a una respuesta antigua de Stack Overflow que recomendaba git filter-branch. Sonaba responsable, pero para un repositorio compartido bajo presión era la decisión equivocada.

El comando se aplicó demasiado ampliamente. En lugar de borrar un solo archivo de forma precisa, reescribió todo el historial del repositorio. Cambiaron los hashes de cuatro años de commits. El historial local divergió del remoto. Y entonces llegó el force push:

git push --force origin main

En ese momento el equipo ya tenía dos incidentes:

  • la filtración original del secreto;
  • y un historial reescrito en un repositorio compartido.

Por qué reescribir el historial compartido de Git es peligroso

Reescribir el historial en una rama que usan otras personas ya es arriesgado por sí mismo. Si lo haces en pánico, una limpieza de seguridad se convierte fácilmente en una avería operativa.

  • Se rompen los repositorios locales. Quienes ya hicieron pull de los commits antiguos dejan de coincidir con origin.
  • Se pierde el contexto de revisión. Pull requests, discusiones y referencias a commits concretos empiezan a apuntar a hashes inexistentes.
  • Se desordena CI/CD. Pipelines, cachés, etiquetas de release y referencias de despliegue pueden dejar de coincidir con el estado real del repositorio.
  • Se complica el debugging. git blame, git bisect y el rastreo de cambios se vuelven menos fiables tras un rewrite innecesario.
  • Aumenta el riesgo de mala recuperación. Si nadie conserva las refs originales, volver al estado previo se complica mucho.

Cómo nos recuperamos

Tuvimos suerte. Un miembro del equipo no había hecho pull después del force push y todavía conservaba una copia local con el historial original y los hashes correctos.

El plan de recuperación fue simple, pero requirió coordinación:

  1. Verificamos que su rama local main coincidía con el último estado remoto correcto.
  2. Volvió a publicar ese historial original al remoto mediante force push.
  3. El resto del equipo ejecutó git fetch origin y resincronizó sus ramas locales.
  4. Comprobamos de nuevo las referencias de pull requests, CI y procesos relacionados.

Si esa copia intacta no hubiera existido, habríamos tenido que reconstruir referencias manualmente o convivir con el historial reescrito.

Diagrama de flujo de la respuesta recomendada ante secretos filtrados en Git: rotar credenciales, eliminar el archivo, añadirlo a gitignore, auditar la exposición y limpiar historial solo con coordinación
Flujo práctico de respuesta cuando se commitean secretos en Git por accidente.

Qué hacer en su lugar si subiste secretos a GitHub por accidente

Después del postmortem documentamos un proceso más seguro. Para la mayoría de equipos y repositorios compartidos, este debería ser el enfoque por defecto.

Paso 1: rota las credenciales inmediatamente

Es la primera acción obligatoria. Revoca el token filtrado, la API key, la contraseña, la clave SSH, el webhook secret o la credencial cloud y emite una nueva. Si el secreto daba acceso amplio, rota también las piezas relacionadas: sesiones, claves de integración o cuentas de servicio.

Importante: eliminar un secreto de Git no lo vuelve seguro. Si ya llegó al remoto, debes asumir que pudo copiarse, quedar en caché o ser detectado por scanners automáticos.

Paso 2: elimina el archivo o el secreto en un commit nuevo

Si el repositorio es compartido, la opción segura por defecto es corregir el estado actual sin reescribir commits antiguos:

git rm config.json
git commit -m "Remove accidentally committed credentials file"
git push origin main

Si el secreto vive dentro de un archivo que sí debe permanecer en el repositorio, elimina el valor sensible, muévelo a variables de entorno o a un gestor de secretos y commitea esa corrección normalmente.

Paso 3: añade una protección para que no vuelva a pasar

Añade el archivo a .gitignore o reestructura la configuración para que los secretos vengan del entorno:

echo "config.json" >> .gitignore
git add .gitignore
git commit -m "Ignore local credentials file"
git push origin main

También ayuda mantener un archivo seguro de ejemplo, como config.example.json o .env.example, para que el equipo sepa qué estructura debe tener la configuración local.

Paso 4: audita el alcance de la exposición

Una vez rotado el secreto y eliminado de la rama actual, toca entender si hubo impacto:

  • Revisa logs del proveedor en busca de solicitudes, IPs o accesos sospechosos.
  • Confirma si el repositorio era público, si fue forkeado, espejado o expuesto en logs de CI.
  • Busca otros secretos hardcodeados dentro del proyecto.
  • Evalúa el scope del secreto filtrado: read-only, write, admin, billing o acceso a datos de producción.
  • Documenta qué se expuso, cuándo se rotó y si hubo señales de abuso.

Paso 5: reescribe el historial solo si realmente hace falta

A veces la limpieza total sí está justificada: repositorios públicos, compliance, exigencias legales o credenciales especialmente sensibles. Pero eso debe tratarse como una operación técnica planificada, no como una reacción emocional.

Si tienes que limpiar el historial:

  • Prefiere git-filter-repo frente a git filter-branch.
  • Apunta solo a la ruta o patrón exacto que necesitas eliminar.
  • Avisa antes a todas las personas que usan el repositorio.
  • Prepara instrucciones de recuperación para clones locales, forks, CI y despliegues.
  • Asume que habrá force push y trabajo manual posterior.
# Eliminar una ruta del historial con git-filter-repo
git filter-repo --invert-paths --path config.json

# Alternativa: BFG Repo-Cleaner
bfg --delete-files config.json

BFG Repo-Cleaner también es válido para limpiezas dirigidas. Lo importante no es tanto la herramienta como el proceso controlado y la coordinación.

Cuándo no reescribir el historial es la mejor decisión

Muchos equipos sobreestiman el valor de borrar el commit antiguo y subestiman el coste de un force push con historial reescrito. Si el secreto ya fue rotado, el riesgo inmediato está en gran parte mitigado. En un repositorio privado y compartido, un forward-fix suele ser el mejor equilibrio.

El orden práctico normalmente es este:

  1. Neutralizar el secreto.
  2. Estabilizar el repositorio.
  3. Solo entonces decidir si la limpieza histórica compensa el daño colateral.

Cómo prevenir commits accidentales de secretos

La mejor respuesta a este tipo de incidente es hacer que el error sea menos probable desde el principio.

Usa .gitignore de forma agresiva

Cualquier repositorio debería ignorar secretos locales comunes desde el primer día:

.env
.env.*
config.json
*.pem
*.key
credentials.json
service-account.json

Usa secret scanning antes del commit y en el push

Los checks previos al commit y en CI detectan muchas filtraciones antes de que lleguen al remoto compartido. Opciones habituales: git-secrets, detect-secrets y Gitleaks.

# Ejemplo: instalar y ejecutar Gitleaks como check pre-commit
brew install gitleaks
gitleaks git --pre-commit

Activa GitHub secret scanning y push protection

GitHub puede detectar muchos formatos conocidos de secretos antes o después de que lleguen al repositorio. Si alojas el código en GitHub, activa secret scanning y, donde esté disponible, push protection.

Guarda los secretos fuera del repositorio

Las credenciales deberían venir de variables de entorno, secretos de plataforma o un gestor de secretos dedicado, no de archivos versionados. Para esto sirven herramientas como HashiCorp Vault, AWS Secrets Manager o 1Password CLI.

Revisa los staged changes antes de cada commit

Este hábito evita más errores de los que parece:

git diff --cached
git log -p -1

FAQ: commiteé secretos en Git por accidente

¿Basta con borrar el archivo?

No. Quitar el archivo del último commit o de la rama actual no hace que el secreto vuelva a ser seguro. Primero rota la credencial y luego limpia el repositorio.

¿Debo hacer force push después de borrar un secreto?

No por defecto. En un repositorio compartido, el force push debe ser la excepción, no el reflejo automático. La mayoría de incidentes se resuelven bien con rotación de credenciales y un commit normal de limpieza.

¿Qué pasa si el repositorio es público?

Asume que el secreto se expuso inmediatamente. Los repositorios públicos se escanean de forma activa. Revoca la credencial en cuanto lo detectes, revisa los logs de uso y luego decide si también necesitas limpieza histórica.

¿Qué herramienta debo usar si hay que limpiar el historial?

Usa git-filter-repo o BFG Repo-Cleaner. Hoy en día git filter-branch rara vez es la mejor opción para este tipo de trabajo.

Conclusiones clave

  1. Primero rotar, después limpiar. El secreto en sí es el incidente. El historial de Git es una tarea de limpieza aparte.
  2. No crees un segundo incidente. Un git push --force precipitado puede causar más daño operativo que el error original.
  3. En repositorios compartidos, prioriza el forward-fix. Elimina el archivo con un commit nuevo, añade protecciones y no bloquees al equipo.
  4. Reescribe el historial solo con intención. Si lo exigen el riesgo, la exposición pública o el compliance, usa la herramienta correcta y coordina el proceso.
  5. La prevención es barata. .gitignore, secret scanning y una gestión sana de configuración evitan la mayoría de estas filtraciones.

Los incidentes de seguridad rara vez quedan definidos solo por el error original. Los define la calidad de la respuesta. Si el equipo rota credenciales rápido, comunica bien y evita romper Git sin necesidad, un commit accidental de secretos se queda en un incidente corto, no en un largo proyecto de recuperación.

Artículos relacionados

Compartir este artículo

LinkedIn X Email

Explorar más

March 24, 2026

Depuración de un sitio en producción tras despliegue con IA — lo que ve el navegador vs lo que desplegaste

Tras desplegar un portfolio construido con IA, encontré menús rotos y contenido desbordado —…

March 21, 2026

Cómo construí y desplegué un tema de WordPress personalizado con agentes de IA en menos de 6 horas

Resumen visual del proyecto: código, resultado final y asistencia de IA en un mismo…

March 21, 2026

Nginx y Apache para rendimiento y escalabilidad

A medida que los negocios y la audiencia se vuelven cada vez más digitales,…