# How Secrets Leak — DevSecOps

Source: https://www.skillbyai.com/en/devsecops/dso-secret-leaks

> Identify the common ways API keys and passwords leak and explain why deleting a committed secret is not enough.

## Secrets travel further than you think

A **secret** is any value that grants access: API keys, database passwords, cloud access keys, private keys, OAuth client secrets, webhook signing keys. They leak in predictable ways: hard-coded in source, committed in a `.env` file, printed in CI logs, baked into a container image layer, pasted into a ticket or chat, or stored in a frontend bundle that every visitor downloads. The most misunderstood case is Git. When you delete a secret in a new commit, it still exists in **history**, and every clone and fork already has a copy. Automated bots scan public repositories continuously, so a leaked cloud key can be abused within minutes. The correct response to a leaked secret is therefore always the same order: **revoke and rotate it first**, check access logs for misuse, then clean history if needed. Rewriting history alone does not make a leaked key safe.

## The life of a secret

A safe secret lifecycle runs from creation in a secret store, to injection at runtime, to regular rotation, to revocation when it is no longer needed or has leaked.

![Four connected stages: create in a store, inject at runtime, rotate, revoke.](assets/figures/devsecops/section-4-map.svg) — Figure 4.1: create -> inject at runtime -> rotate -> revoke.

## Proving the secret is still in history

Deleting the line does not delete the past. `git log -p` shows the old diff.

```bash
git init -q demo && cd demo
git config user.email dev@example.com && git config user.name dev
echo 'API_KEY=demo-not-a-real-key-123' > .env
git add .env && git commit -qm "add config"
git rm -q .env && git commit -qm "remove secret"
test -e .env || echo ".env is gone from the working tree"
git log -p --all | grep 'API_KEY'
```

Output:

```
.env is gone from the working tree
-API_KEY=demo-not-a-real-key-123
+API_KEY=demo-not-a-real-key-123
```

## Docker layers remember too

`COPY .env .` followed by `RUN rm .env` still leaves the file in an earlier image layer. Use build secrets (`RUN --mount=type=secret`) and a `.dockerignore` instead.

**Quiz:** You notice a cloud access key was pushed to a public repository an hour ago. What should you do first?

- [ ] Rewrite Git history to remove it
- [x] Revoke and rotate the key, then review its usage logs
- [ ] Make the repository private and continue
- [ ] Add the file to .gitignore

*Answer:* Revoke and rotate the key, then review its usage logs. Once public, assume it is compromised. Revoking stops abuse; history cleanup and prevention come after.
