Articles / Security

  • ssh
  • cryptography
  • linux
  • security
  • openssh
  • hardening

Which SSH Ciphers, MACs and Key Exchanges to Switch Off

A default install of OpenSSH will still happily offer key exchange methods, ciphers and MACs that were reasonable a decade ago and are not reasonable now. Nobody is going to break AES-256-GCM, but plenty of servers still advertise diffie-hellman-group14-sha1 and hmac-sha1 to any client that asks. There is no good reason to keep doing that.

Most SSH hardening advice stops at authentication: use keys, not passwords, maybe add a certificate authority or a hardware key. That is the right priority, but it leaves a second layer completely untouched: the actual cryptographic algorithms your SSH daemon is willing to negotiate.

This is a walkthrough of the directives in sshd_config that control this:

  • KexAlgorithms
  • Ciphers
  • MACs
  • plus the related HostKeyAlgorithms

The goal is a config that only negotiates algorithms you'd be happy to defend, and a way to check that before you trust it with your only route into a box.

Find out what your server offers before changing anything

Before changing anything, find out what is actually being negotiated. From the server itself, sshd -T dumps the effective configuration, resolving all the defaults:

sudo sshd -T | grep -Ei '^(kexalgorithms|ciphers|macs|hostkeyalgorithms)'

From a client, you can list what your local ssh binary supports (not necessarily what a given server offers) with:

ssh -Q kex
ssh -Q cipher
ssh -Q mac

To see what a remote server actually proposes during negotiation, without needing an account on it, the most convenient tool is ssh-audit. It connects, performs the initial handshake, and grades every algorithm the server offers as good, weak or outright dangerous:

ssh-audit example.com

Run that against a box you have not touched in a while and you will usually get a small shock.

Five kinds of algorithm to remove on sight

A few categories of algorithm are worth removing on sight, for reasons that are not really in dispute:

  • SHA-1-based key exchange. diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1 combine a hash that is cryptographically broken for collision resistance with, in the group1 case, a 1024-bit modulus that is within reach of nation-state-level compute. Neither belongs on an internet-facing server in 2026.
  • CBC-mode ciphers (aes256-cbc, aes128-cbc and friends) are vulnerable to padding-oracle style attacks in the SSH context and have been superseded for years by authenticated modes.
  • RC4 (arcfour, arcfour128, arcfour256) has known keystream biases. It has no place anywhere.
  • Non-ETM (encrypt-then-MAC) MACs and anything based on MD5 or truncated SHA-1 (hmac-md5, hmac-sha1-96, umac-64) provide weaker integrity guarantees than the ETM variants, which authenticate the ciphertext rather than the plaintext and are not vulnerable to certain length-extension and error-oracle issues.
  • ssh-rsa as a signature algorithm (not the RSA key type itself) relies on SHA-1. OpenSSH 8.8 disabled it by default back in 2021, in favour of the RFC 8332 rsa-sha2-256 and rsa-sha2-512 signature schemes, which use the same RSA keys with a stronger hash. If you have not touched your config since before that release, you may still have compatibility shims re-enabling it.

A baseline config for a modern OpenSSH

Here is a reasonable baseline for /etc/ssh/sshd_config on a modern Linux box (OpenSSH 9.x), assuming you do not need to talk to genuinely ancient clients:

KexAlgorithms [email protected],curve25519-sha256,[email protected],diffie-hellman-group16-sha512,diffie-hellman-group18-sha512

Ciphers [email protected],[email protected],[email protected],aes256-ctr,aes192-ctr,aes128-ctr

MACs [email protected],[email protected]

HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256

A few notes on the choices:

  • Order matters: OpenSSH negotiates the first mutually supported algorithm in your list, so put your genuine preference first. [email protected] is a hybrid classical/post-quantum key exchange (Streamlined NTRU Prime combined with X25519); it costs almost nothing in a terminal session and protects against "harvest now, decrypt later" against a future quantum adversary, so there is little reason not to prefer it where both ends support it.
  • Dropping plain CTR-mode ciphers entirely is defensible too if every client is recent, but AES-CTR without a paired ETM MAC is not itself broken the way CBC is, so it is a reasonable middle tier to keep rather than a hill to die on.
  • The client side has the same directives in ~/.ssh/config or /etc/ssh/ssh_config, which matters if you are also hardening the machines you SSH from, not just the ones you SSH to.

Terrapin: why "strict KEX" isn't optional

One specific, fairly recent vulnerability changes what "hardened" actually means here. In December 2023, researchers disclosed Terrapin (CVE-2023-48795), a prefix-truncation attack against the SSH transport protocol itself, not against any specific weak algorithm.

A man-in-the-middle who can manipulate packet sequence numbers during the unauthenticated part of the handshake can silently delete a chosen number of messages sent immediately after key exchange, before encryption fully kicks in.

The practical impact includes stripping the RFC 8308 extension negotiation. In some client configurations, that can be used to force a downgrade of keystroke-timing obfuscation, or of the signature algorithm back towards SHA-1.

Here's the ironic bit: the ciphers most exposed were the modern-looking ones. That means [email protected] (because of how it handles sequence numbers internally) and any cipher paired with an ETM MAC, which is exactly the combination this article just recommended above.

The fix isn't to abandon those algorithms. It's a protocol-level countermeasure called "strict KEX", shipped in OpenSSH 9.6, which does two things:

  • resets sequence numbers after SSH2_MSG_NEWKEYS
  • terminates the connection on any out-of-order message during the handshake

It only takes effect when both ends support it, advertised via the pseudo-algorithms [email protected] and [email protected]. OpenSSH adds these automatically, and you do not configure them yourself.

The takeaway is concrete: algorithm selection is necessary but not sufficient. Check your OpenSSH version is at least 9.6 (or that your distribution has backported the fix, as most did quickly given the severity) before you consider ChaCha20-Poly1305 and the ETM MACs genuinely safe to prefer.

ssh -V
sshd -V

Testing the change without locking yourself out

Restarting sshd with a bad config is the classic way to lock yourself out of a remote box. Before you do it:

  1. Keep your current SSH session open. Do not close it until you have confirmed a new connection works.
  2. Validate syntax first: sudo sshd -t checks the config file without restarting anything.
  3. Restart, don't reload where possible, so a syntax error surfaces immediately: sudo systemctl restart sshd.
  4. From a second terminal, open a fresh connection and confirm it succeeds, then re-run ssh-audit against the host to confirm the weak algorithms have actually disappeared, not just been reordered.
  5. Only then close the original session.

If you manage more than one host, this is also a good candidate for a small Ansible or shell-script rollout with a delay and automatic rollback. A cron job that reverts sshd_config and restarts the service after five minutes, cancelled once you confirm access, beats trusting yourself to remember step 5 under pressure at 2am on a server three time zones away.

Quick detour: there are two lists of signature algorithms

One last thing worth checking: PubkeyAcceptedAlgorithms (formerly PubkeyAcceptedKeyTypes) controls which signature algorithms are accepted for public key authentication specifically. That's separate from HostKeyAlgorithms, which governs the server's own identity key.

It is easy to harden one and forget the other. Doing so leaves a mismatched, harder-to-debug set of allowed algorithms depending on whether the client is authenticating the server or itself.