---
title: "Eliminar secretos del historial de Git con git filter-repo"
description: "¿Subiste una clave o contraseña en un commit? Revócala, quítala del historial de Git con git-filter-repo, limpia GitHub y evita la próxima filtración."
url: https://example-petstore.com/es/guides/remove-secrets-from-git
language: es
---

# Eliminar un secreto del historial de Git

Una clave, un token o una contraseña acabó en un commit. Borrar el archivo en un commit nuevo no sirve: el commit antiguo lo sigue conteniendo, en cada clon y cada fork. Esta guía muestra el orden correcto: primero revocar, después reescribir el historial, limpiar la plataforma de alojamiento y asegurarte de que no vuelva a pasar.

## Primero: revoca el secreto

Considera expuesto cualquier secreto que haya llegado a un commit, incluso en un repositorio privado: los clones, los forks, los registros de CI y las copias de seguridad pueden tener ya una copia, y los escáneres automáticos detectan los commits nuevos de los repositorios públicos en cuestión de minutos.

1. Revoca o renueva la clave, el token o la contraseña en el servicio que los emitió.
2. Guarda la nueva credencial en la configuración o en un gestor de secretos, no en el código.
3. Revisa los registros del servicio en busca de usos que no reconozcas.

Dónde revocar claves en los proveedores más habituales: [Credenciales filtradas: qué hacer ahora](https://example-petstore.com/es/guides/leaked-credentials). Reescribir el historial viene después, nunca en su lugar.

## Reescribir el historial o no

Un secreto revocado ya no da acceso, y GitHub señala que eso puede bastar. Aun así, reescribir merece la pena cuando:

- el secreto no se puede revocar, o el archivo contiene otros datos sensibles, como datos personales o registros de clientes;
- el repositorio es público o lo será;
- quieres que los escáneres dejen de avisar del secreto antiguo.

Reescribir cambia el ID de todos los commits posteriores. Quienes colaboran tienen que hacer rebase de su trabajo, las pull requests abiertas pueden perder sus comentarios de revisión y se eliminan las firmas de los commits. Acuerda un momento con todas las personas implicadas, y fusiona o cierra antes las pull requests abiertas.

## Encontrar cada aparición

```
# ¿Qué commits añadieron o eliminaron la cadena?
git log --all --oneline -S 'the-secret-value'

# ¿En qué archivos aparecía?
git grep 'the-secret-value' $(git rev-list --all)

# Analizar todo el historial en busca de formatos de secretos conocidos
gitleaks git -v
```

Anota cada ruta de archivo en la que apareció el secreto, incluidos los nombres anteriores si el archivo se movió o se renombró.

## Reescribir con git-filter-repo

`git-filter-repo` es la herramienta que recomienda GitHub. Usa la versión 2.47 o posterior, que incluye la opción `--sensitive-data-removal`, y trabaja en un clon nuevo.

```
# Instalar (o usar tu gestor de paquetes)
brew install git-filter-repo        # macOS
pip install git-filter-repo         # en cualquier sistema con Python

git clone https://github.com/YOUR-ORG/YOUR-REPO
cd YOUR-REPO

# Opción 1: eliminar un archivo entero de todo el historial
git-filter-repo --sensitive-data-removal --invert-paths --path config/secrets.yml

# Opción 2: sustituir el secreto en todos los lugares donde aparece
git-filter-repo --sensitive-data-removal --replace-text ../replacements.txt
```

El archivo de sustituciones contiene un valor por línea. De forma predeterminada, cada coincidencia pasa a ser `***REMOVED***`; con `==>` eliges tú la sustitución, y `regex:` busca un patrón:

```
# ../replacements.txt (fuera del repositorio)
sk_live_51Hx0000000000000000000000
AKIA0000000000000000==>AWS_ACCESS_KEY_ID_REMOVED
regex:password\s*=\s*"[^"]+"==>password = "REMOVED"
```

Comprueba el resultado con `git log --all -S 'the-secret-value'`: no debería mostrar nada. Después, sobrescribe el remoto:

```
git push --force --mirror origin
```

Si la protección de ramas bloquea los force push, tendrás que desactivarla durante ese momento. Después del push, la reescritura ya no se puede deshacer.

## Limpiar GitHub

Después del force push, los commits antiguos siguen siendo accesibles a través de pull requests, vistas en caché y forks.

- **Pull requests y vistas en caché:** ponte en contacto con el soporte de GitHub e indica el número de pull requests afectadas (`grep -c '^refs/pull/.*/head$' .git/filter-repo/changed-refs`) y los «First Changed Commit(s)» que mostró git-filter-repo. GitHub solo ayuda cuando rotar la credencial no basta para eliminar el riesgo.
- **Forks:** los commits de los forks se quedan ahí. Pide a sus propietarios que eliminen o limpien el fork; GitHub no comparte sus datos de contacto.
- **Clones de tus compañeros:** todo el mundo debe hacer rebase de sus ramas sobre el nuevo historial, no merge. Un solo merge trae de vuelta los commits antiguos.

En GitLab, Bitbucket o un servidor propio, los pasos son parecidos; consulta la documentación de esa plataforma para saber cómo elimina los objetos antiguos y las vistas en caché.

## Alternativa: BFG

BFG Repo-Cleaner es una herramienta más antigua, basada en Java, que todavía se usa mucho. Trabaja sobre un clon espejo y, de forma predeterminada, no toca el último commit, así que primero quita el secreto de la versión actual con un commit normal.

```
git clone --mirror https://github.com/YOUR-ORG/YOUR-REPO.git
java -jar bfg.jar --replace-text replacements.txt YOUR-REPO.git
cd YOUR-REPO.git
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push
```

## Evitar la próxima filtración

- **Protección de push:** en GitHub, el escaneo de secretos con protección de push bloquea un push que contiene un formato de secreto conocido antes de que llegue al repositorio.
- **Una comprobación antes de cada commit:** ejecuta gitleaks o git-secrets como hook de pre-commit, y en la CI para quien se lo salte.
- **Configuración, no código:** lee los secretos de variables de entorno o de un gestor de secretos, y añade `.env` y archivos similares a `.gitignore` antes del primer commit.
- **Revisa antes de hacer commit:** prepara los archivos uno a uno y comprueba `git diff --cached`, en lugar de usar `git add .` o `git commit -a`.

¿Esa clave también se envió a una dirección de ejemplo como api.example-petstore.com? Entonces también llegó a un servidor que no controlas: [Configurar clientes de API y SDK](https://example-petstore.com/es/guides/api-base-url).

## Guías relacionadas

### [Credenciales filtradas: qué hacer ahora](https://example-petstore.com/es/guides/leaked-credentials)

Se enviaron claves, tokens o contraseñas a un dominio de ejemplo. Revócalos y sustitúyelos, con enlaces directos a las páginas de revocación de los proveedores más habituales.

### [Configurar clientes de API y SDK](https://example-petstore.com/es/guides/api-base-url)

Mantén las URL base fuera del código, haz que apunten al servicio real y añade una comprobación que impida que las direcciones de ejemplo lleguen a producción.

## Fuentes

- [Removing sensitive data from a repository](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository) GitHub Docs
- [About push protection](https://docs.github.com/en/code-security/secret-scanning/introduction/about-push-protection) GitHub Docs
- [git-filter-repo](https://github.com/newren/git-filter-repo) GitHub
- [BFG Repo-Cleaner](https://rtyley.github.io/bfg-repo-cleaner/) Roberto Tyley
- [Gitleaks](https://github.com/gitleaks/gitleaks) GitHub
