# how to respond to a leaked cloud key in a public repo

## TL;DR

Assume the key is compromised the second it hits a public repo. Revoke it first, investigate second. Then check what the key touched while it was live, scrub it from git history so it stops spreading, and put a secrets check in place so it cant happen again. Speed matters more than elegance here.

```text
how to respond to a leaked cloud key in a public repo
```

## Use this when

- A cloud credential (AWS, GCP, Azure, or any API key) was pushed to a public repo
- A scanner, a teammate, or a stranger emails you about a leaked secret
- You are not sure whether the key was actually used by anyone else
- You need a repeatable leak-response runbook for your team

## Not for this skill when

- The leak was in a private repo with no outside access (lower urgency, still rotate)
- You need key rotation without downtime (that is a separate rotation skill)
- The leaked thing was a password, not a cloud key (passwords get reset, keys get revoked)
- You are setting up secret scanning in CI (prevention, not response)

## Steps

### 1. Identify exactly what leaked

Figure out the key type, which account and user it belongs to, and how long it has been public. Check the commit that introduced it and whether it is still in the current HEAD.

```bash
git log -S "[key-id-or-unique-prefix]" --oneline --all
```

Expected: one or more commits touching the key. Note the oldest commit date; that is the start of your exposure window. If the key was in an old commit but removed later, it is still exposed; git history is public until scrubbed.

### 2. Revoke the key now

Do not wait for the investigation. Disable or delete the key in the cloud console or CLI, then issue a replacement through your normal provisioning flow.

```bash
aws iam delete-access-key --access-key-id [KEY_ID] --user-name [iam-user]
```

Expected: the command succeeds and the key stops working immediately. Confirm with a read-only call using the old key and check that it fails. Revoking first is safe even if the investigation later shows no abuse; a fresh key costs you minutes.

### 3. Check what the key did while it was live

Look at audit logs for the exposure window: what the key accessed, from which IPs, and whether anything looks off. Also check billing for surprise spend.

```bash
aws cloudtrail lookup-events --start-time 2026-09-01T00:00:00Z --max-items 200 | tee trail.json | grep -c "[KEY_ID]"
```

Expected: a count of events made with that key, or 0 if it was never used. Any nonzero count is worth a closer look; pull the full events and check the source IPs, and widen the investigation if any IP is unfamiliar. Other clouds have the same idea under different names: GCP Cloud Audit Logs, Azure Activity Log.

### 4. Scrub the key from git history

Revoking stops the bleeding; scrubbing stops the secret spreading to every clone and fork. BFG rewrites history replacing the key material everywhere it appears.

```bash
printf '[leaked-key-material]\n' | tee replacements.txt
bfg --replace-text replacements.txt myrepo.git
```

Expected: BFG reports the key replaced across all commits, and searching history afterward finds nothing. Force-push the cleaned branches and tell every contributor to re-clone; anyone holding an old clone still has the key. History rewrites need team coordination, so announce it first. (BFG repo-cleaner; git-filter-repo is the equivalent alternative.)

### 5. Put a secrets check in the repo

Add a pre-commit or CI check that scans for secrets before they land. One scan in the pipeline beats a hundred incident responses.

```bash
trufflehog git file://. --only-verified --fail
```

Expected: trufflehog exits 0 on a clean repo and nonzero when it finds a verified secret. Add the same scan to CI so future leaks get caught before merge. (trufflehog 3.x; gitleaks is a lighter alternative.)

### Variant: leaked key in a fork or PR you do not control

You cannot rewrite someone else's repo. Revoke the key and check for abuse as usual, then ask the repo owner to scrub it, or file a takedown with the host if they do not respond. The revoke is what protects you; the scrub is hygiene.

### Variant: key embedded in a shipped artifact

Docker images, packages, and mobile builds can carry the key too. Search your registries and published artifacts for the key prefix, rotate anything bundled with it, and cut a new release. Assume every copy of the artifact is public.

### Variant: you got the exposure email from the cloud provider

AWS, GitHub, and others proactively notify you when they find your keys in public places. Treat it as confirmed exposure and start at step 2; the provider already did step 1 for you.

## Why this happens

Keys end up in repos because the path of least resistance is pasting them into a config file to make something work, and the .gitignore rule gets forgotten or the file was committed before the rule existed. Once pushed to a public repo, bots scan new commits for key patterns within minutes; "it was only up for an hour" is not a defense. The fix is procedural, not heroic: revoke fast, then make the repo unable to accept secrets.

## Edge cases and pitfalls

- Multi-region and multi-account keys: revoke everywhere the key existed, not just the first account you checked.
- Keys baked into running instances: revoking can break prod; have the replacement ready and deploy it in the same change window.
- Files committed before a .gitignore rule: git keeps tracking files it already knows; the ignore rule does not retroactively remove them.
- Private repos that later go public: audit for secrets before flipping the visibility switch.
- Forks: scrubbing your repo does not scrub forks; the revoke is the real protection.
- Do not "test" a leaked key by using it; any use confirms it is live and can look like abuse in the audit log.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_9O_GuseHH2LE0xF6K8HMbA
