---
title: "Subiste el .env a GitHub: qué pasa en los primeros minutos"
description: "Borrar el archivo no borra la key. Qué hacer, en qué orden, y por qué rotar va antes que limpiar el historial."
canonical: "https://vtsecurity.com.ar/articulo/subiste-el-env-a-github"
author: "Valentín Torassa"
lang: "es-AR"
datePublished: "2026-09-15T02:17:21.653Z"
dateModified: "2026-09-15T02:17:21.653Z"
---

# Subiste el .env a GitHub: qué pasa en los primeros minutos

> Borrar el archivo no borra la key. Qué hacer, en qué orden, y por qué rotar va antes que limpiar el historial.

Pasa así: armás un proyecto chico, ponés la API key en un `.env`, te olvidás del `.gitignore` y hacés `git push`. Diez minutos después te das cuenta, borrás el archivo, commit, push. En la pestaña de GitHub el `.env` ya no está.

Y sin embargo la key sigue ahí. No es un bug de GitHub ni mala suerte: es cómo funciona Git. Este artículo es sobre esos minutos y sobre qué hacer, en orden.

## El archivo que borraste

Git no guarda archivos sueltos: guarda una foto del proyecto entero en cada commit. Cuando borrás el `.env`, creás una foto nueva sin el archivo. Las anteriores siguen existiendo, y la pestaña de GitHub te muestra solo la última, `HEAD`.

Fijate en qué fotos queda el `.env`. Con las flechas podés volver al primer commit, y el último paso muestra qué se lleva alguien que clona el repo.

*Repo de ejemplo. Cada hoja es la foto que guarda un commit.*

Un clone trae todas las fotos, no solo la última. Cualquiera puede buscar el archivo en el historial y leerlo:

```bash
$ git log --all --oneline -- .env
e05d2a8 borro el .env
7c2e4b1 conecto la API
$ git show 7c2e4b1:.env
OPENAI_API_KEY=sk-proj-7Hq2…
```

> *Pasarlo a privado tampoco alcanza.* Los forks y los clones que ya existen tienen su copia, y no dependen de tu configuración.

## Quién mira un push público

Un repo público no es público solo para personas. Hay scanners que leen el feed de eventos públicos de GitHub y buscan cadenas con forma conocida: `AKIA` en las keys de AWS, `ghp_` en los tokens de GitHub, `sk-` en varios proveedores de IA. No necesitan entrar a tu repo: les llega el diff.

La pregunta útil no es si alguien la vio. Es cuánto tiempo pasó entre el push y el momento en que vos te diste cuenta: esa es la ventana de exposición. Mové tu marca y mirá qué alcanzó a pasar.

*Tiempos ilustrativos. Varían según el tipo de secreto.*

Del otro lado, GitHub tiene *secret scanning*: para algunos proveedores le avisa al emisor de la key, que puede revocarla sola. Para otros no pasa nada. Por eso lo que importa es qué podía hacer esa key.

| Secreto | Forma | Avisa GitHub | Qué hacer primero |
| - | - | - | - |
| Access key de AWS | `AKIA…` | sí | Desactivarla en IAM y mirar CloudTrail |
| Token de GitHub | `ghp_…` | sí | Revocarlo y generar otro con scopes mínimos |
| Key de IA | `sk-…` | depende | Revocarla y poner límite de gasto |
| Contraseña de la base | ninguna | no | Cambiarla y revisar conexiones |

## Rotar antes que limpiar

El instinto es reescribir el historial para que no quede rastro. Para mí ese es el error más común: se siente como arreglarlo, y no lo es. Mientras reescribís, la key sigue siendo válida en cada copia que ya existe, y ninguna reescritura llega hasta ahí.

Probá los dos órdenes. La key vive en el proveedor; las líneas rojas son las copias que todavía pueden usarla. Rotar la mata en todas a la vez.

*Los minutos son un ejemplo: reescribir, forzar el push y avisarle al equipo lleva su rato.*

1. Revocar o rotar la key en el proveedor. Antes que cualquier otra cosa.
2. Revisar el uso desde el momento del push: facturación, CloudTrail, el panel que tenga el proveedor.
3. Sacar el archivo del historial, recién ahora.
4. Frenar el próximo push antes de que salga de tu máquina.

## Sacarlo del historial

Para reescribir uso `git filter-repo`, que reemplazó a `filter-branch`. Cambia el hash de cada commit desde el que tocó el archivo, así que cualquiera que trabaje en el repo va a tener que volver a clonar.

Dos cosas te van a frenar la primera vez. Sobre un repo en el que venís trabajando, `filter-repo` no arranca:

```
Aborting: Refusing to destructively overwrite repo history since
this does not look like a fresh clone.
```

Está pensado para correr sobre un clone recién hecho. Si lo hacés sobre el tuyo, va `--force`. Y cuando termina te borra el remoto `origin` a propósito, para que no pushees de memoria; si no lo volvés a agregar, el push falla con `No configured push destination`.

```bash
# primero: ¿en qué commits está?
git log --all --full-history --oneline -- .env

# después de rotar la key, recién ahí
git filter-repo --invert-paths --path .env --force
git remote add origin git@github.com:vos/tu-repo.git
git push --force --all
```

Los hashes cambian todos, incluso los de commits anteriores al que tocó el archivo.

> *Una aclaración.* Si el repo es de un equipo, avisá antes del `--force`. Al que tenga cambios sin pushear le vas a romper la tarde.

## Que no vuelva a pasar

Lo más barato es que el commit no salga. Un hook de pre-commit con `gitleaks` mira lo que estás por commitear y frena si encuentra algo con forma de secreto.

```yaml title=".pre-commit-config.yaml"
# frena el commit si encuentra algo con forma de secreto
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.30.1
    hooks:
      - id: gitleaks
```

No hace falta pasarle `--redact`: el hook ya corre `gitleaks git --pre-commit --redact --staged`, así que lo que encuentra sale tapado y no te deja el secreto en la salida de la terminal.

Y agregá `.env` al `.gitignore` antes del primer commit, con un `.env.example` sin valores para que se entienda qué variables hacen falta.

## El criterio

Si una key tocó un repo público, aunque sea un minuto, tratala como comprometida. No hay forma de probar que nadie la leyó, y rotarla cuesta menos que averiguarlo.
