In short: To find unknown SSH keys, you must check all authorized_keys files on the server, not just root's. Use sshd -T to determine which files your SSH service reads, list the fingerprint of every key and compare them with the known keys. Secure foreign entries first, then remove them and harden access afterward.
The key that survives every password change
After an incident, many operators first change all passwords. Against one of the most inconspicuous backdoors, that does not help: an additional public key in an authorized_keys file. Whoever holds the matching private key logs in without a password, until somebody finds the line.
Unknown SSH keys are therefore among the signs that should always be checked after code execution. For PHP2Shell there are no published, confirmed indicators of compromise; detection relies on behavioral analysis. A foreign key is not specific PHP2Shell evidence, but a clear sign that someone wanted lasting access. Further signs are described in Am I already compromised?.
Where SSH keys are on the server
According to its documentation, OpenSSH by default reads two files in each user's home directory: .ssh/authorized_keys and .ssh/authorized_keys2. The second is often forgotten. The setting can be changed, and with AuthorizedKeysCommand the server can even obtain keys from an external program. So first check what actually applies on your server:
# Print the effective SSH configuration (read-only)
sudo sshd -T | grep -iE '^(authorizedkeysfile|authorizedkeyscommand|permitrootlogin|passwordauthentication|pubkeyauthentication)'
# Additional configuration files and Match blocks
ls -la /etc/ssh/sshd_config.d/ 2>/dev/null
sudo grep -rn -iE '^\s*(Match|AuthorizedKeys)' /etc/ssh/
A special location is the home directory of the web server user. On Debian and Ubuntu, that is often /var/www for www-data. If this directory is writable for PHP, injected code can create a .ssh/authorized_keys there. Whether logging in then works depends on the login shell and SSH settings, but the file itself is already an alarm sign.
Not every unknown entry is malicious. Deployment tools, backup services or former service providers deposit keys of their own. But such entries should have a known owner and purpose.
Checking authorized_keys: step by step
The following commands change nothing. They list all users' key files and show the fingerprint of each key:
# All authorized_keys files in the home directories of all users
getent passwd | cut -d: -f1,6 | while IFS=: read -r user home; do
for f in "$home/.ssh/authorized_keys" "$home/.ssh/authorized_keys2"; do
[ -f "$f" ] && echo "== $user: $f" && sudo ls -la --time-style=full-iso "$f"
done
done
# Show fingerprints and comments of a file
sudo ssh-keygen -lf /root/.ssh/authorized_keys
# Further key files outside the usual locations
sudo find / -xdev -name 'authorized_keys*' 2>/dev/null
Go through the result with this checklist:
- Does every key belong to a known person or a known service? Compare the fingerprints, not just the comment.
- Does a user have keys that should have none at all? For example the web server user, database or service accounts.
- Are there options in front of the key? Entries with
command=run a fixed command at every login. That is legitimate for backups, but also a hiding place. - When was the file last changed? A modification date after 17 Oct 2026, the first PHP2Shell attack attempt observed according to media reports, deserves special attention. Timestamps can be forged, though.
- Do owner and permissions match? A file that belongs to a different user than the home directory stands out.
Removing foreign keys safely
Before you delete a line, secure the file with timestamp and checksum outside the server. Note the fingerprint of the foreign key. It later helps with the question of whether the same key also turns up on other servers, for example at an agency with many customer systems.
Also check the login logs. In /var/log/auth.log (Debian, Ubuntu) or /var/log/secure (RHEL family), the SSH service logs successful logins with the fingerprint of the key used. That way you can see whether the foreign key has already been used and from which address.
Also think of the opposite direction. If private keys are on the server, for example under ~/.ssh/id_* for deployments or backups, an attacker could have copied them. With them he may reach other servers, Git repositories or backup targets. After a break-in, such keys count as spent: generate new keys and remove the old ones on all target systems.
A foreign key is rarely the only trace. Whoever deposited it had write permissions and may have set up further routes, such as cron jobs or additional accounts. So also check hidden cron jobs and rogue admin accounts. If the attacker had root rights, rebuilding the server is often more reliable than cleaning up.
Remove foreign keys only once PHP is at a fixed version (8.5.12, 8.4.27, 8.3.36, 8.2.35 or a confirmed backport) and all workers have been restarted. Otherwise the attacker can immediately enter the key again through the same vulnerability.
Hardening SSH access afterward
After the cleanup, a brief hardening is worthwhile. Most points are quickly implemented but should always be tested with a second, open session so that you do not lock yourself out.
| Measure | Setting or procedure |
|---|---|
| Login by key only | PasswordAuthentication no |
| Do not allow direct root login | PermitRootLogin no, work via personal accounts with sudo |
| Switch off the second key file | AuthorizedKeysFile .ssh/authorized_keys |
| Limit keys by origin | Option from= before the key, for example for backup servers |
| Restrict service keys | Option restrict and a fixed command= for automation |
| Web server user without login | Login shell nologin, no .ssh in the home directory |
Also keep a list of all authorized keys with owner and purpose. When an employee or service provider leaves, their key is removed on all servers. Without this list, the next check is detective work again. Recognized hardening guidelines for SSH are also contained in the CIS Benchmarks.
How does PHP SOS help?
- We determine which key files your SSH service really reads and check every entry against the known owners.
- We secure findings and evaluate the login logs to clarify whether a foreign key was used.
- We clean up keys, accounts and cron jobs in the right order, closing the vulnerability beforehand.
- Within server hardening, we implement the SSH settings and set up key management with an owner list.
Frequently asked questions
How do I tell whether an SSH key is foreign?
Every key has a fingerprint and usually a comment at the end of the line. Compare the fingerprints with the keys of your employees and service providers. The comment alone proves nothing, because it can be chosen freely and forged.
Does changing the root password help?
Not against an injected key. Logging in with a key needs no password. That is why the authorized_keys files themselves must be checked and cleaned.
Why can the web server user have an SSH key?
The web server user has a home directory of its own, often /var/www on Debian and Ubuntu. If a .ssh/authorized_keys sits there and the account has a login shell, logging in as this user is possible. With a writable web directory, injected PHP code can create such a file.
What is authorized_keys2?
An older second file that OpenSSH also reads by default. Many checking guides overlook it. Use sshd -T to check which files your server actually evaluates.