# Leaked SSH Private Key? Rotate It and Check Access

> SSH or RSA private key pushed to GitHub? Remove it from every authorized_keys, replace it, check logs and clean up. Step-by-step guide.

Source: https://leakwatch.net/secrets/ssh-private-key

---

[LeakWatch](/)

[Product](/product)[Live feed](/leaks)[Guides](/secrets)[Blog](/blog)[Free scan](/)

1.  [Home](/)
2.  [Secrets](/secrets)
3.  SSH / RSA Private Key

Databases & keys · Secret guide

# Leaked SSH / RSA Private Key: what to do in the first hour

Critical severityChecked liveLast verified October 2, 2026 · 5 min read

A private key is the half of a key pair that proves who you are. If it is exposed, anyone can log in to every place that trusts its public half, with no password prompt and nothing to guess. There is no provider to ask for a new one and no “revoke” button: you have to remove the matching public key from every place that trusts it, then generate a new pair. That list of places is the real work, so start by finding it.

[Check your GitHub account for leaked secrets — free](/)

Provider

Other

Severity

Critical

Impact

Source code and build access

Checked live by LeakWatch

Yes

## What a private key looks like

Private keys are text files that start and end with a marker line. The line tells you the format:

```text
-----BEGIN OPENSSH PRIVATE KEY-----   modern OpenSSH format (Ed25519, RSA, ECDSA)
-----BEGIN RSA PRIVATE KEY-----        RSA, older PEM format
-----BEGIN EC PRIVATE KEY-----         elliptic-curve, PEM format
-----BEGIN PRIVATE KEY-----            PKCS#8, any algorithm
-----BEGIN ENCRYPTED PRIVATE KEY-----  PKCS#8 protected by a passphrase
```

Between the markers is a block of base64 text; this page never shows one, and neither should you paste one anywhere. A `.pub` file, or a line starting with `ssh-ed25519` or `ssh-rsa`, is the **public** key and is safe to share.

Do not assume a `BEGIN PRIVATE KEY` block is an SSH key. The same format holds **TLS certificate keys**, **JWT signing keys** and **code-signing keys**, and each one is revoked differently (see the FAQ). PGP keys have their own `BEGIN PGP PRIVATE KEY BLOCK` header and a separate LeakWatch rule.

## How private keys get leaked

-   **A whole `~/.ssh` directory in a repository.** Often via a dotfiles repo, or a `git add .` in the home folder.
-   **Keys baked into Docker images and CI.** A `COPY id_rsa` or an `ARG` in a Dockerfile (they remain in image layers), or a deploy key pasted into a workflow file instead of the secrets store.
-   **Backups and archives.** A zipped project folder, a backup of a laptop or server pushed to a public repository or bucket.
-   **Test fixtures and sample configs.** A real key used to “make the example work” in a `tests/` or `examples/` folder.
-   **Pasted for help.** In an issue, a chat or a forum post, while asking why authentication fails.

## What to do in the first hour

1.  **Identify the key and what trusts it.** Work out the public key that goes with it. If you still have the private file, `ssh-keygen -y -f <file>` prints the public key and `ssh-keygen -l -f <file>` prints its fingerprint. Then list where it might be installed: servers (`~/.ssh/authorized_keys` of each user, and `/root/.ssh/authorized_keys`), GitHub and GitLab (account SSH keys and repository **deploy keys**), cloud key pairs, CI/CD settings, Git hosting for other teams, and any bastion hosts.
2.  **Remove the public key from every one of those places.** On servers, delete its line from `authorized_keys`. On GitHub, open *Settings* → *SSH and GPG keys*, and repository *Settings* → *Deploy keys*. Do this before you generate a replacement, so there is no window where the leaked key still works. Deleting a key pair in a cloud console does **not** remove it from instances that already have it, so check the instances too.
3.  **Generate a new pair, protected by a passphrase.** `ssh-keygen -t ed25519 -C "<purpose>"` creates one, and the passphrase prompt protects the file if it leaks again. Use a separate key per purpose (one per server group, one for CI) so a single leak does not unlock everything.
4.  **Look for logins made with the old key.** On Linux servers, `sshd` logs accepted public-key logins with the key fingerprint, in `/var/log/auth.log` or via `journalctl -u ssh` (or `sshd`). Search for the fingerprint from step 1. Also look at `last` for unfamiliar sources.
5.  **Look for persistence on any machine it could reach.** An attacker who got in often adds their own key to `authorized_keys`, creates a user, adds a cron job or a systemd unit. If you find any of these, treat the machine as compromised and rebuild it rather than clean it.
6.  **Rotate what lived on those machines.** Environment variables, config files and anything else readable by the login user.
7.  **Then clean the repository**: remove the file, add it to `.gitignore`, and rewrite history if you want to. See [I accidentally pushed an API key to GitHub](/blog/i-accidentally-pushed-an-api-key-to-github).

Not sure what else leaked from the same repository? [Run a free scan](/).

[Not sure what else leaked? Run a free scan.](/)

## Revoke it at the places that trust the key

A key has no issuer, so you revoke it by removing trust. For SSH, that means deleting the public key from each `authorized_keys` file, from your Git hosting account and deploy keys, and from any CI or cloud setting that holds it; there is no central list to query. The fingerprint from step 1 is your search term. If the key was a server **host key** (in `/etc/ssh/ssh_host_*_key`) rather than a login key, regenerate the host keys on that server and let your users know their clients will warn about the change.

## How LeakWatch detects it

LeakWatch matches a block that starts with a `BEGIN … PRIVATE KEY` marker and ends with the matching `END` marker. It does **not** check this type live, and it could not: a private key is proof of possession, not a token you can send to a server and ask “is this still active?”, and testing it would mean using someone else’s key to sign in. Instead LeakWatch analyzes the key itself, offline: whether it is a real key rather than a template or a truncated block, and whether it is protected by a passphrase. It can tell you the key is exposed, not whether it still opens anything. Assume it does until you have removed it everywhere.

LeakWatch can check whether a detected key is still active with a read-only request to Other. It never reads your data or spends your credits.

## FAQ

My key has a passphrase. Is it still a leak?

Yes, but a less urgent one. The attacker has to guess the passphrase offline first, and a long random one makes that impractical. A weak passphrase will not hold for long. Rotate the key either way: you cannot know how strong the passphrase really is.

The block starts with BEGIN PRIVATE KEY. Is it still SSH?

Not necessarily. If it is a TLS certificate key, ask the certificate authority to revoke the certificate and issue a new one with a new key. If it signs tokens (JWT), rotate it and invalidate sessions it signed. If it is a code-signing key, contact the issuing authority. Check how you used it before deciding.

Can I just delete the key from the server and keep using the same pair elsewhere?

No. The point of rotating is that the exposed pair stops being trusted anywhere. Create a new pair and move every use to it.

## Related

-   [Database Connection String](/secrets/database-connection-string)
-   [Google API Key](/secrets/google-api-key)
-   [Airtable Personal Access Token](/secrets/airtable-personal-access-token)
-   [I accidentally pushed an API key to GitHub](/blog/i-accidentally-pushed-an-api-key-to-github)
-   [Which vendors leak most this week](/leaks/trends)
-   [All secret guides](/secrets)

[Get alerted next time a secret leaks — create a free account](/)

LeakWatch is not affiliated with Other.

![Gabriel Diyan, founder of LeakWatch](/brand/Photo-Gabriel-Diyan.webp)

Gabriel Diyan (0xCr0c0)

Cybersecurity student, founder of LeakWatch. I built and run the scanner described here — the detection patterns, the false-positive classifier and the provider validators are mine. [More about who I am](/about).

[GitHub](https://github.com/Leakwatch-Scan) · [X](https://x.com/LeakwatchScan) · [LinkedIn](https://www.linkedin.com/in/gabriel-diyan-80a378375) · [GitHub (personal)](https://github.com/crocogab)

LeakWatch

Secrets leak into public commits every minute. This watches the forges for yours. Built and run by [Gabriel Diyan](/about), a cybersecurity student — [why LeakWatch exists](/about).

Scan

-   [Product](/product)
-   [Live feed](/leaks)
-   [Trends](/leaks/trends)
-   [API docs](/docs)
-   [CI/CD](/docs?tab=ci)

Read

-   [Blog](/blog)
-   [Secret guides](/secrets)
-   [Changelog](/changelog)
-   [About](/about)

Verify

-   [Security](/security)
-   [Privacy](/privacy)
-   [Terms](/terms)
-   [Legal](/legal)
-   [Contact](/contact)
-   [Status](https://status.leakwatch.net)

© 2026 LeakWatch

[GitHub](https://github.com/Leakwatch-Scan)[X](https://x.com/LeakwatchScan)
