Back to blog

What Happens When an SSH Key Is Compromised?

What an SSH key compromise really means in practice, the risks that follow, how to respond, and why knowing where each key is trusted makes incident response much easier.

An SSH key compromise can go one of two ways. Sometimes it is a quick, contained bit of cleanup. Other times it turns into a week of hunting through servers, second-guessing yourself, and hoping you did not miss anything.

What decides which one you get is usually pretty simple: do you actually know where that key is used?

If the key belongs to one person, sits on two servers, and only opens a limited account, you are in good shape. Revoke it, replace it, check the logs, done. But if you cannot say for certain which servers trust it, which accounts it can reach, or whether it got copied around over the years, the same incident becomes a slow, nervous cleanup. That situation almost always comes from scattered authorized_keys files and stale SSH keys nobody has looked at in a long time.

This post covers what can actually happen when a key is compromised, the risks worth taking seriously, how to work through the response, and why visibility is the thing that makes all of it easier.

What "compromised SSH key" usually means

When people say a key has been compromised, they almost always mean the private key is now in the wrong hands, or somewhere they no longer trust.

That covers a lot of ground. A laptop gets stolen, a private key ends upaccidentally being committed to a Git repo, or copied onto a shared drive, or even worse, pasted into a chat converation! Maybe a backup full of keys gets exposed, or a contractor reuses the same key somewhere they should not have or someone leaves the team and you would rather their key stopped working.

The public key is a different story. Public keys are meant to be shared and typically sit in authorized_keys on the servers that trust them. The private key is the part that matters. If someone holds a private key and the matching public key is trusted somewhere, they can potentially log in as whatever account that public key is allowed to use. Everything below follows from that one fact.

Risk 1: direct server access

The obvious risk is that someone can now SSH into a box. If the compromised key is trusted for a Linux account, an attacker may be able to log in as that account, depending on your network setup, SSH configuration, and whether the key needed a passphrase.

Where the key lives tells you how bad it is. A key in /home/deploy/.ssh/authorized_keys gets someone in as deploy. The same key in /root/.ssh/authorized_keys is a much bigger problem, because it may hand over direct root login. And if that key is sitting on ten servers instead of one, an attacker potentially has all ten.

This is the real cost of key reuse. One compromised key is manageable. One compromised key that gets used across production, staging, client servers, deployment boxes, and some old infrastructure nobody thinks about is a fleet-wide cleanup, and it is one of the common SSH access mistakes that quietly makes incidents worse.

Risk 2: data exposure

Once someone is on a server, the next question is what that account can read. Even a stanrard account with no root access often still has access to plenty: application source, .env files, API tokens, database credentials, backups, logs full of user data, uploaded files, internal scripts, deployment secrets, and sometimes other SSH keys the server uses to reach further.

When you've got a compromised key, it doesn't just expose a machine - it exposes everything that machine can touch. This matters most on application servers, which tend to be stuffed with credentials for other systems. A web server knows how to reach the database. A deployment server holds Git credentials. A backup server has copies of production data. A monitoring server knows about everything. The key might only open one door, but there can be a lot waiting behind it.

Risk 3: privilege escalation

A non-root account is not a dead end. If it can run sudo, an attacker may be able to escalate. Sometimes sudo needs a password, sometimes it does not, sometimes it is scoped to a couple of commands, and sometimes it is far wider than anyone intended.

The quickest way to see where you stand is:

sudo -l

That lists what the current user can run with sudo. It is also worth reading through /etc/sudoers and anything under /etc/sudoers.d/, because that is where the surprises usually hide.

The question to answer is whether this key could realistically reach root. If it can, treat the whole thing as more serious. And keep in mind escalation is not only about sudo: write access to deployment scripts, cron jobs, service files, or application code can be just as good, because all of those influence what the server runs.

Risk 4: lateral movement

A compromised key is often a starting point rather than the whole story. Once someone is on one server, they will look for a way to the next one, and servers are full of those paths: SSH keys and agent-forwarding sockets, deployment and Git credentials, cloud provider tokens, database passwords, access to a private network, shared passwords, backup credentials, and environment or config files with secrets baked in.

You can see how it compounds. Someone lands on a staging box with the compromised key. That box has database credentials and a deploy key for a Git repo. They use those to reach source code and other systems, and from there they find more secrets. Not every compromised key ends in a major breach, but it is a mistake to treat SSH access as isolated. Servers usually trust other servers, and that trust travels.

Risk 5: persistence

If an attacker gets in, they will often try to make sure they can get back in even after the original key is gone. That might mean dropping in another SSH key, creating a new user, changing an existing user's shell or permissions, adding a cron job, editing startup scripts, installing a service, or quietly modifying application code.

This is why pulling the compromised key is only step one. Removing the key is containment: it closes that specific way back in. It does not tell you whether something changed while the key still worked, and that is the part you actually have to go and check.

Working through the response

The exact steps depend on your environment, but the shape of a good response is fairly consistent.

1. Identify the key

Start by pinning down the compromised public key. If you have the private key, you can derive the matching public key:

ssh-keygen -y -f compromised_private_key

And get its fingerprint:

ssh-keygen -lf compromised_key.pub

Lean on the fingerprint rather than the comment. Comments get changed, duplicated, or dropped, but the fingerprint identifies the key itself. If you want a quick read of the key type, length, and fingerprint, the SSH key inspector does it in the browser.

2. Find where the key is trusted

This is the part that decides how your day goes. You need every place the matching public key appears, and that list is longer than most people expect: user and root authorized_keys files, deployment and automation accounts, client servers, forgotten staging boxes, cloud images and templates, config management, key-installing scripts, hosting control panels, and even the internal doc that tells people to paste the key in.

On a single server you can grep for it:

sudo grep -R "PUBLIC_KEY_COMMENT_OR_FRAGMENT" /home/*/.ssh/authorized_keys /root/.ssh/authorized_keys

Search on a chunk of the key body, not the comment. The comment is there for humans and makes a terrible identifier, since it is easy to change or leave off entirely.

For a more thorough sweep, how to audit authorized_keys files and how to audit SSH access across your fleet both walk through the process, and the authorized_keys analyzer is handy for reading a single file quickly.

3. Remove the key everywhere

Once you know where it is trusted, take it out of every authorized_keys file or, better, every source of truth that feeds them.

Be careful with anything managed by automation. If a key is installed by Ansible, Terraform, cloud-init, a server template, or a hosting platform, editing authorized_keys by hand only fixes things until the next deploy puts the key right back. Remove it from whatever is placing it there, or it will quietly return.

4. Replace the key if access is still needed

If the person genuinely still needs access, do not resurrect the old key. Generate a fresh pair instead. Use a modern type like Ed25519, give it a real passphrase, store it somewhere sensible, keep it to one person, do not reuse it across unrelated environments, and only add it where access is actually required.

Treat the compromised key as dead, not inconvenient. It is tempting to keep it alive because reissuing is a hassle, but that is exactly how a "temporary" problem becomes permanent. If you want a safe command for the replacement, the SSH keygen command generator covers the common options.

5. Review recent SSH activity

Next, work out whether the key was actually used. How far you can get depends on your logging, but the usual places to look are:

/var/log/auth.log
/var/log/secure
journalctl -u ssh
journalctl -u sshd

You are scanning for successful and failed logins, connections from odd IPs or at odd hours, sudo usage, new users, edits to authorized_keys or sudoers files, unexpected service restarts, and any suspicious commands your logging happened to capture. A successful login will not always tell you which key was used unless you configured it to, but it still helps you narrow down the window and whether anything happened at all.

6. Check for privilege changes and persistence

After the key is gone, look for anything that changed while it worked. Start with the account and privilege files:

/etc/passwd
/etc/shadow
/etc/group
/etc/sudoers
/etc/sudoers.d/

Check the SSH directories for keys you did not put there:

/home/*/.ssh/
/root/.ssh/

Check scheduled tasks:

crontab -l
sudo ls -la /etc/cron.*
sudo ls -la /var/spool/cron/

Check services:

systemctl list-unit-files

And look at what was touched recently in sensitive locations:

sudo find /etc -type f -mtime -7
sudo find /home -type f -mtime -7
sudo find /root -type f -mtime -7

None of this is a full forensic investigation, but it is a solid first pass. If the key had production or privileged access, plan for something a lot deeper.

7. Rotate related secrets

If the key reached a server that holds secrets, assume those secrets were read. That means rotating database passwords, API tokens, cloud credentials, Git deploy keys, CI/CD tokens, backup credentials, application secrets, any SSH keys stored on the box, and any third-party service credentials it could reach.

Teams underestimate this step constantly. The key is the entry point, but the credentials sitting on the server are often worth far more than the server itself.

8. Write it down

Keep a record of what happened: which key it was, who owned it, when it was reported, where it turned out to be trusted, which servers and accounts were affected, when you removed it, whether it looked like it had been used, what you rotated, and what is still outstanding.

It does not need to be a formal report for every minor scare. But six months from now, when someone asks whether that key was ever dealt with, "I think so" is not an answer you want to be giving.

Why visibility is the hard part

The technical cleanup is rarely the difficult bit. The difficult bit is the uncertainty.

When you do not know where a key is used, you end up guessing your way through a pile of questions. Which servers might trust it? Which accounts hold it? Did it end up in root? Was it used for deploys? Did it get added to client servers, or installed by Ansible, or reused across staging and production? Is it sitting on some box nobody has logged into in two years? Every one of those questions is a delay, and delays are the last thing you want during a compromise.

Really, you want to answer three things fast:

  1. Where is this key trusted?
  2. What access does it give?
  3. Has it been removed everywhere?

If you can answer those quickly, the response is almost calm. If you cannot, you are stuck checking servers one by one, digging through old scripts, asking around to see if anyone recognises a key, and hoping nothing slipped through.

Where GrantSSH fits

GrantSSH exists to make SSH access something you can see and change from one place, instead of something spread across authorized_keys files and people's memory. You manage which people have access to which servers and Linux accounts centrally, which is exactly the visibility that a key compromise demands.

In practice that means you can see where a person's access reaches, revoke it in one place, and let the server agent clean up the matching authorized_keys entries for you. Scheduled access helps prevent the whole problem too, since temporary access expires on its own rather than lingering for months. And because GrantSSH records activity like logins and sudo usage, you have real context when you are trying to reconstruct what happened. The features page goes into the access control, scheduling, and auditing side in more detail.

None of this replaces a proper incident response. You still have to investigate affected systems, look for persistence, and rotate exposed secrets. What it removes is the worst part: not knowing where access exists in the first place.

The short version

A compromised key is serious, but it does not have to descend into chaos. The clearer your SSH access is, the calmer the response.

What you really want, before anything goes wrong, is to know who owns each key, which servers trust it, which accounts it can reach, whether it is still needed, when it should expire, whether it has been used recently, and how to pull it fast. When that lives across servers, scripts, old notes, and memory, cleanup is slow and uncertain. When it lives in one place, it is mostly just a task.

That is the whole lesson, really. When a key is compromised, the damage depends on what it can access, and your response depends on how quickly you can find and remove that access.

Next steps

Take control of your SSH access

GrantSSH gives teams a clear, auditable way to grant and revoke SSH access. Create your account and get started in minutes.

← Back to blog