OpenSSH started in September 1999, when OpenBSD developers forked a cleaned up copy of the last freely licensed ssh release (1.2.12) and shipped it as OpenSSH 1.2.2 with OpenBSD 2.6 on 1 December 1999. As of 25 October 2026 the newest release is OpenSSH 10.6 (6 October 2026). Across those 27 years almost every major version tightened a default: privilege separation in 3.3, SSH protocol 1 off in 5.4 and gone by 7.6, SHA-1 RSA signatures off in 8.8, post-quantum key exchange by default in 9.0, DSA removed and ML-KEM made the default in 10.0.
Below, era by era, are the defaults and design changes, taken only from the OpenSSH release notes and project pages, the OpenBSD manual pages and the announcement lists. Individual vulnerabilities are out of scope. For the protocol itself, read our SSH protocol explainer; for the latest fixes, read which OpenSSH 10.6 security fixes apply.
Key takeaways
- OpenSSH was forked from Björn Grönvall's OSSH, itself built on Tatu Ylönen's ssh 1.2.12. The initial import into OpenBSD was on 26 September 1999, and OpenSSH 1.2.2 shipped with OpenBSD 2.6 on 1 December 1999.
- There are two release lines. OpenBSD gets the plain version (10.6). Every other operating system gets the portable release with a "p" suffix (10.6p1). The portable page says the two are not released in lockstep.
- Privilege separation became the default in 3.3 (June 2002) and impossible to turn off in 7.5 (March 2017). In 9.8 (July 2024) and 10.0 (April 2025) sshd was split further into the sshd, sshd-session and sshd-auth binaries.
- Key exchange moved from finite field Diffie-Hellman to ECDH in 5.7 (2011), Curve25519 in 6.5 (2014), the sntrup761x25519 hybrid in 9.0 (2022) and mlkem768x25519-sha256 in 10.0 (2025). Since 10.1 the client warns when a connection is not post-quantum.
- Old algorithms were retired in stages. DSA was disabled at run time in 7.0 (2015), disabled at compile time in 9.8 and removed in 10.0. RSA signatures with SHA-1 ("ssh-rsa") were disabled by default in 8.8 (2021).
- PerSourcePenalties (9.8) makes sshd refuse addresses that keep failing authentication or crash the server. It is on by default.
- Distribution packages backport fixes without changing the upstream version, so a version string alone does not prove a host is patched.
How did OpenSSH begin?
According to the OpenSSH history page, Tatu Ylönen's ssh 1.2.12 was the last release "free enough for reuse"; later versions banned Windows and DOS ports and then limited commercial use. Early in 1999 Björn Grönvall picked up the old free release, fixed bugs and called it OSSH. It spoke only SSH 1.3 and never gained SSH 2.
OpenBSD developers found OSSH less than two months before the OpenBSD 2.6 release, forked it and audited it the way they audited the rest of OpenBSD. The initial import was on 26 September 1999. Theo de Raadt removed non-portable code so the source was easier to audit, Niels Provos removed the remaining cryptographic and GPL licensed parts, and Markus Friedl added SSH 1.5 support, later SSH 2 and SFTP. The result was OpenSSH 1.2.2, shipped with OpenBSD 2.6 on 1 December 1999. The OpenBSD 2.6 release page calls it "the most interesting addition" and notes it was free for everyone except US commercial users, because the RSA patent was still in force.
When did SSH 2 and SFTP arrive?
The history page says OpenSSH 2.0, which spoke both protocol versions, shipped with OpenBSD 2.7 on 15 June 2000. The OpenBSD 2.7 release page lists the bundled version as OpenSSH 2.1, so the two pages differ on the number; both agree SSH 2 arrived in June 2000. The SFTP server shipped in 2.3.0 in November 2000 and Damien Miller's sftp client in 2.5.0.
How do the OpenBSD and portable releases relate?
Damien Miller, Philip Hands and others began porting OpenSSH to Linux and other Unix systems almost immediately. The project kept the core code free of operating system differences and put portability work in a separate tree. The portable release page explains:
- Normal development produces a small version for OpenBSD.
- The portability team adds code for Linux, macOS, Solaris, AIX, Cygwin and others.
- Portable releases carry a "p" suffix, such as 4.0p1. OpenBSD never uses it.
- The two lines are not released in sync.
This matters for security work: some release note fixes are marked "Portable OpenSSH only" because the bug lived in portability code such as PAM support. The portable tree's history is public in the openssh-portable repository, and since 9.8 its signed release branch includes generated files so it matches the signed tarball exactly.
OpenSSH release timeline: every major security change
The table lists each release that changed a security relevant default or added a security feature. Dates come from the headers in the OpenSSH release notes, except the two OpenBSD era entries, which come from the history page and the OpenBSD release pages.
| Version | Date | Security defaults and features changed |
|---|---|---|
| 1.2.2 | 1 Dec 1999 | First release, shipped with OpenBSD 2.6; patent encumbered parts removed |
| 2.0 | 15 Jun 2000 | SSH protocol 2 support (shipped with OpenBSD 2.7) |
| 2.3.0 | Nov 2000 | SFTP server |
| 3.3 | 21 Jun 2002 | Privilege separation enabled by default; ssh no longer needs setuid root for protocol 2 hostbased auth (ssh-keysign) |
| 5.4 | 8 Mar 2010 | SSH protocol 1 disabled by default; OpenSSH certificates for users and hosts; PKCS#11 token support |
| 5.7 | 24 Jan 2011 | ECDSA keys and ECDH key exchange; ECDH preferred when both sides support it |
| 5.9 | 6 Sep 2011 | Sandboxing of the pre-authentication privilege separated child |
| 6.2 | 22 Mar 2013 | AES-GCM, encrypt-then-MAC modes, AuthenticationMethods for multi-factor login, Key Revocation Lists |
| 6.5 | 30 Jan 2014 | curve25519-sha256@libssh.org key exchange (default), Ed25519 keys, bcrypt protected private key format, chacha20-poly1305 cipher |
| 6.9 | 1 Jul 2015 | chacha20-poly1305 promoted to default cipher |
| 7.0 | 11 Aug 2015 | Protocol 1 disabled at compile time; ssh-dss and diffie-hellman-group1-sha1 disabled at run time; PermitRootLogin default becomes prohibit-password |
| 7.2 | 29 Feb 2016 | Client disables blowfish-cbc, cast128-cbc, arcfour and MD5 and truncated HMACs by default; RFC 8332 RSA SHA-2 signatures supported |
| 7.4 | 19 Dec 2016 | Server support for protocol 1 removed; 3des-cbc removed from client defaults; server pre-authentication compression removed; curve25519-sha256 name |
| 7.5 | 20 Mar 2017 | UsePrivilegeSeparation deprecated, so privilege separation is mandatory |
| 7.6 | 3 Oct 2017 | Protocol 1 deleted; arcfour, blowfish, CAST and hmac-ripemd160 removed; RSA keys under 1024 bits refused; client stops offering CBC ciphers |
| 7.8 | 24 Aug 2018 | ssh-keygen writes the OpenSSH private key format by default |
| 8.0 | 17 Apr 2019 | Default RSA key size raised to 3072 bits; experimental sntrup4591761x25519 post-quantum key exchange |
| 8.2 | 14 Feb 2020 | FIDO/U2F security keys (ecdsa-sk, ed25519-sk); ssh-rsa removed from accepted certificate signature algorithms |
| 8.5 | 3 Mar 2021 | sntrup761x25519-sha512@openssh.com replaces the older hybrid; UpdateHostKeys on by default |
| 8.8 | 26 Sep 2021 | RSA signatures with SHA-1 (ssh-rsa) disabled by default |
| 8.9 | 23 Feb 2022 | ssh-agent key forwarding and use restrictions; sntrup761 hybrid added to default list |
| 9.0 | 8 Apr 2022 | sntrup761x25519-sha512 becomes default key exchange; scp uses the SFTP protocol by default |
| 9.1 | 4 Oct 2022 | RequiredRSASize sets a minimum RSA key length |
| 9.5 | 4 Oct 2023 | ssh-keygen generates Ed25519 keys by default; keystroke timing obfuscation in the client |
| 9.6 | 18 Dec 2023 | "Strict KEX" protocol extension hardens the initial key exchange |
| 9.7 | 11 Mar 2024 | DSA becomes compile-time optional |
| 9.8 | 1 Jul 2024 | sshd split into sshd and sshd-session; PerSourcePenalties on by default; DSA disabled at compile time |
| 9.9 | 19 Sep 2024 | mlkem768x25519-sha256 available by default; client pre-authentication compression removed |
| 10.0 | 9 Apr 2025 | DSA removed; mlkem768x25519-sha256 default; sshd-auth binary; finite field DH removed from server defaults; AES-GCM preferred over AES-CTR |
| 10.1 | 6 Oct 2025 | Client warns on non post-quantum key exchange (WarnWeakCrypto); agent sockets move from /tmp to ~/.ssh/agent; XMSS removed |
| 10.2 | 10 Oct 2025 | Bugfix release (ControlPersist regression) |
| 10.3 | 2 Apr 2026 | Empty certificate principals no longer match any user; no more bug compatibility for peers that cannot rekey; invaliduser penalty |
| 10.4 | 6 Jul 2026 | Non-KEX messages during rekey cause disconnect; Linux seccomp setup failures become fatal; experimental ML-DSA 44 plus Ed25519 signatures |
| 10.5 | 11 Aug 2026 | Portable build requires elliptic curve support (including NIST P-521) in libcrypto |
| 10.6 | 6 Oct 2026 | LZ77 dictionary compression disabled to stop a cross-channel side channel; ssh-mldsa44-ed25519 enabled under its IANA name; WarnWeakCrypto added to sshd |
If you build your own timeline from the release notes page, note that the headings for 3.7.1, 3.1 and 3.0.2 carry dates that sit out of order with their neighbours, and that 4.8 was an OpenBSD only release (with OpenBSD 4.3) on the same day as 4.9.
The early years: 1999 to 2011
Why was privilege separation such a big step?
Before 3.3, the network facing sshd process ran as root through the whole login exchange, so any memory bug in code that parsed attacker data before login meant root compromise. Privilege separation splits sshd into a small privileged monitor and an unprivileged child that speaks the network protocol. The 3.3 release notes list it first: "privilege separation is now enabled by default". 3.3 also added ssh-keysign so ssh no longer needed setuid root for protocol 2 hostbased authentication.
The 7.4 notes remark that privilege separation "has been enabled by default for 14 years". 7.5 made it mandatory and 9.8 removed the last code paths for running without it.
How did 5.x tighten things?
- 5.4 (March 2010) disabled SSH protocol 1 by default "after a transition period of about 10 years". It also replaced the old smartcard code with PKCS#11 support and added OpenSSH certificates: a minimal format, not X.509, where a CA key signs a user or host key plus identity and validity constraints. 5.6 revised the format to the one used today.
- 5.7 (January 2011) added ECDSA keys and ECDH key exchange from RFC 5656, with ECDH preferred when both sides support it.
- 5.9 (September 2011) sandboxed the unprivileged pre-authentication child (systrace on OpenBSD, seatbelt on macOS, an rlimit fallback). 6.0 added a Linux seccomp filter sandbox and 6.1 turned sandboxing on by default for new installs.
Certificates let a team issue short lived user keys and trust one CA key instead of copying public keys everywhere. See SSH certificates versus authorized_keys.
The modern crypto era: 6.x and 7.x
What did 6.2 and 6.5 change?
6.2 (March 2013) added AES-GCM authenticated encryption, encrypt-then-MAC modes (such as umac-128-etm@openssh.com), the AuthenticationMethods option for requiring more than one login method in sequence, and Key Revocation Lists.
6.5 (January 2014) shaped today's defaults more than any other release:
- curve25519-sha256@libssh.org key exchange, the default when both sides support it.
- Ed25519 for user and host keys.
- A private key format protected by a bcrypt key derivation function, used for Ed25519 keys and made the default for all key types in 7.8 (August 2018).
- The chacha20-poly1305@openssh.com cipher, promoted to default in 6.9 (July 2015).
Why was 7.0 a crypto cleanup release?
The 7.0 notes (August 2015) say the release focused on deprecating "weak, legacy and/or unsafe cryptography":
- Protocol 1 disabled at compile time.
- diffie-hellman-group1-sha1 (1024 bit) and ssh-dss (DSA) keys disabled at run time.
- The legacy v00 certificate format removed.
- PermitRootLogin default changed from "yes" to "prohibit-password", which now blocks all interactive methods for root.
7.0 also introduced the "+" prefix: HostKeyAlgorithms=+ssh-dss appends to the defaults instead of replacing them, so the rest of the list keeps tracking upstream. The legacy options page still recommends this for one-off exceptions.
How did 7.x finish removing protocol 1 and weak ciphers?
- 7.2 (February 2016) disabled blowfish, CAST, arcfour, the rijndael-cbc aliases and MD5 and truncated HMACs in the client (sshd already had them off).
- 7.4 (December 2016) removed server support for protocol 1 and dropped 3des-cbc from client defaults, because 64 bit block ciphers were no longer safe and the team did not want to wait for attacks like SWEET32 to reach SSH. The notes warn that 3des-cbc was the only mandatory cipher in the SSH RFCs, so old devices might fail. 7.4 also removed pre-authentication compression from sshd.
- 7.5 (March 2017) made privilege separation mandatory.
- 7.6 (October 2017) deleted protocol 1 from the client, removed hmac-ripemd160 and the arcfour, blowfish and CAST ciphers, refused RSA keys shorter than 1024 bits and stopped offering CBC ciphers by default.
The 8.x era: hardware keys and the end of SHA-1
When did FIDO security keys arrive?
8.2 (February 2020) added FIDO/U2F hardware authenticators through two new key types, ecdsa-sk and ed25519-sk, with matching certificate types. The private key is split between a key handle in a file and a secret that never leaves the token, and signing needs the token present and, by default, a touch. 8.2 also added no-touch-required for automation, resident keys on FIDO2 tokens and optional attestation output. See our posts on ed25519-sk security keys and requiring a key and a second factor.
8.2 also stopped accepting ssh-rsa (RSA with SHA-1) for certificate signatures, because, as the notes explain, someone forging a certificate has unlimited time to compute a collision.
Why did 8.8 disable ssh-rsa?
8.2 had warned that chosen-prefix SHA-1 collisions cost under 50,000 US dollars, citing the 2020 "SHA-1 is a Shambles" paper. 8.8 (September 2021) disabled RSA signatures using SHA-1 by default. The key type did not change: an RSA key now signs with rsa-sha2-256 or rsa-sha2-512 (RFC 8332), supported since 7.2, and the notes say there is "no need to replace ssh-rsa keys".
What breaks is a peer that can only sign with SHA-1, such as old embedded servers and network gear. The 8.8 notes give a per-host exception and recommend it only "as a stopgap measure". Our post on Ed25519 versus RSA keys covers which key to generate today.
What else hardened keys and agents in 8.x?
- 8.0 (April 2019) raised the default RSA key size to 3072 bits, citing NIST SP 800-57, and added an experimental post-quantum hybrid, sntrup4591761x25519.
- 8.5 (March 2021) replaced it with sntrup761x25519-sha512@openssh.com and enabled UpdateHostKeys by default under conservative preconditions, so clients can learn a server's newer host keys.
- 8.9 (February 2022) added restrictions on where keys in ssh-agent can be used and forwarded, and put the sntrup761 hybrid in the default list. If you forward agents, read what the OpenSSH manual warns about agent forwarding.
The 9.x era: post-quantum by default and a smaller attack surface
What did 9.0 change?
9.0 (April 2022) changed two defaults that affect almost everyone:
- Post-quantum key exchange. sntrup761x25519-sha512@openssh.com pairs Streamlined NTRU Prime with X25519, so it is at least as strong as the X25519 default it replaced. The notes say the change came early to stop "capture now, decrypt later" attacks.
- scp over SFTP. Legacy scp expanded remote wildcards through the remote shell, so shell metacharacters in file names had to be double quoted or they could run as commands. Over SFTP the quoting is unnecessary and may now break transfers.
scp -Ofalls back to the old protocol.
What changed between 9.1 and 9.7?
- 9.1 (October 2022) added RequiredRSASize for a minimum RSA key length.
- 9.5 (October 2023) made ssh-keygen generate Ed25519 keys by default and added keystroke timing obfuscation (ObscureKeystrokeTiming): interactive traffic goes out at fixed intervals, 20 ms by default, with fake "chaff" keystrokes after typing stops.
- 9.6 (December 2023) added the "strict KEX" extension, which forbids unexpected messages during the initial key exchange and resets message counters after each one. The notes say this should stop the Terrapin attack, explained in our Terrapin post. It only works when both ends support it.
- 9.7 (March 2024) made DSA compile-time optional and announced its removal in 2025.
How did 9.8 change sshd's architecture?
9.8 (July 2024) split the server into a small listener, sshd, and a per-connection binary, sshd-session. The listener no longer speaks the SSH protocol at all. The same work removed the last ways to disable privilege separation or sshd re-execution. 9.8 also disabled DSA at compile time and introduced PerSourcePenalties, covered below.
9.9 (September 2024) added mlkem768x25519-sha256, a hybrid of FIPS 203 ML-KEM and X25519, available by default, and removed pre-authentication compression from the client.
The 10.x era: 2025 and 2026
What changed in OpenSSH 10.0?
10.0 (April 2025) has the longest incompatible changes list of any recent release:
- DSA removed, "completing the deprecation process that began in 2015".
- mlkem768x25519-sha256 is the default key exchange, described as no weaker than curve25519-sha256, standardised by NIST and faster than the previous default.
- sshd-auth. User authentication moved out of sshd-session into its own binary, so the most exposed pre-authentication code has an address space of its own. Distributors must package the new binary.
- Finite field Diffie-Hellman off in sshd. The diffie-hellman-group* and group-exchange methods left the server's defaults; the client still offers them. If /etc/moduli exists but has no group in the requested range, sshd no longer falls back to built in groups.
- Cipher order. The client prefers chacha20-poly1305, then AES-GCM, then AES-CTR.
- The version string. The notes warn that software matching patterns like "OpenSSH_1*" may be confused by "OpenSSH_10.0". Scanners and inventory scripts are that kind of software.
What did 10.1 and 10.2 add?
10.1 (October 2025) made the client warn when a connection does not use post-quantum key exchange, saying the session "may be vulnerable to store now, decrypt later attacks". The new WarnWeakCrypto option controls the warning and defaults to on. 10.1 also moved agent sockets from /tmp to ~/.ssh/agent, so processes that can reach /tmp no longer get ambient access to keys; made ssh-add expire certificates in the agent shortly after the certificate itself expires; and removed experimental XMSS keys. 10.1 and 10.2 both warn that SHA-1 SSHFP DNS records will be deprecated. 10.2 (October 2025) was a quick bugfix release.
What did 10.3 through 10.6 change?
- 10.3 (April 2026): a certificate with an empty principals list, trusted through an authorized_keys
principals=""option, used to match any user, which the notes call "surprising and potentially risky". Now it matches nothing. 10.3 also dropped bug compatibility for peers that cannot rekey, added aninvaliduserpenalty and validated names passed to-Jon the command line. - 10.4 (July 2026) disconnects peers that send non key exchange messages during a rekey, makes Linux seccomp setup failures fatal, and added experimental ML-DSA 44 plus Ed25519 composite signatures, off by default.
- 10.5 (August 2026) requires elliptic curve support, including NIST P-521, in the libcrypto that portable OpenSSH uses.
- 10.6 (October 2026) disabled the LZ77 dictionary coder in SSH compression. The notes cite a 2026 paper showing that, because all channels share one compression dictionary, attacker data on one channel could reveal secrets on another through ciphertext lengths; they recommend application level compression instead. 10.6 also enabled the ssh-mldsa44-ed25519 signature algorithm under its IANA name (keys from the experimental version must be regenerated), added WarnWeakCrypto to sshd and refuses command line usernames containing backslash or dollar characters.
The OpenBSD 8.0 announcement (20 October 2026) lists OpenSSH 10.6. OpenBSD 7.8 (22 October 2025) shipped 10.2 and OpenBSD 7.9 (19 May 2026) shipped 10.3.
How did key exchange evolve from Diffie-Hellman to ML-KEM?
Key exchange is the step that decides whether recorded traffic stays private. Each generation replaced the last because of cost, strength or a new threat.
| Generation | First in OpenSSH | Default from | Status now | Why it changed |
|---|---|---|---|---|
| diffie-hellman-group1-sha1 (1024 bit) | Original protocol 2 | Early 2000s | Off by default since 7.0 | Too small; the legacy page mentions the Logjam attack |
| Other finite field DH groups and group exchange | Early releases | Before ECDH | Removed from sshd defaults in 10.0; client still offers them | Slow and expensive for the security they give |
| ecdh-sha2-nistp256/384/521 | 5.7 (2011) | 5.7 | Still in the default list | Faster elliptic curve agreement |
| curve25519-sha256 | 6.5 (2014) | 6.5 | Still in the default list | Simpler, safer curve design |
| sntrup761x25519-sha512 | 8.5 (2021), experimental predecessor in 8.0 | 9.0 (2022) | Second in the default list | First post-quantum hybrid default |
| mlkem768x25519-sha256 | 9.9 (2024) | 10.0 (2025) | First in the default list | NIST standardised ML-KEM, faster than sntrup761 |
The current sshd_config manual page lists the server default KexAlgorithms as mlkem768x25519-sha256, then sntrup761x25519-sha512 (under both names), curve25519-sha256 (both names) and the three NIST ECDH curves. The client picks the order.
What does the OpenSSH post-quantum page recommend?
The post-quantum page gives a short recommendation: all SSH connections should use one of the two post-quantum key agreement algorithms. Its main points:
- Key agreement is urgent because of "store now, decrypt later": recorded sessions can be decrypted once a capable quantum computer exists. The page cites estimates of 5 to 20 years.
- Signatures are less urgent, since there is no record now, forge later equivalent; classical signature keys just need retiring in time.
- If you see the 10.1 warning, the server offered neither mlkem768x25519-sha256 (9.9 and later) nor sntrup761x25519-sha512 (9.0 and later). Upgrade it, or check whether its KexAlgorithms setting removed them.
- If you must accept the risk, silence the warning per host with a
Match hostblock andWarnWeakCrypto no-pq-kex.
On signatures, 10.4 added an experimental ML-DSA 44 and Ed25519 composite and 10.6 enabled it under its IANA name.
Which algorithms were deprecated, and what breaks?
Every retirement followed the same pattern: announce in one release, disable by default in a later one, remove in a later one still. This table collects the main ones.
| Algorithm | Warned | Disabled by default | Removed | What usually breaks |
|---|---|---|---|---|
| SSH protocol 1 | Years before 5.4 | 5.4 (2010), compile time in 7.0 | Server 7.4, client 7.6 | Very old devices and clients |
| ssh-dss (DSA keys) | 7.0 notes, 9.7 to 9.9 notices | 7.0 (2015) at run time, 9.8 at compile time | 10.0 (2025) | Old network gear, old DSA user keys, legacy host keys |
| diffie-hellman-group1-sha1 | 7.0 | 7.0 | Not removed; re-enable per host | Old embedded servers |
| ssh-rsa (RSA with SHA-1 signatures) | 8.2 (2020) | 8.8 (2021); certificates already in 8.2 | Not removed | Peers that never implemented RFC 8332 |
| 3des-cbc | 7.4 | 7.4 in client | Still available | Devices that only implement the mandatory RFC cipher |
| blowfish, CAST, arcfour | 7.0 notice | 7.2 in client | 7.6 | Very old peers |
| CBC ciphers in general | 7.5 notice | 7.6 in client | Still available on request | Old appliances |
| hmac-ripemd160, MD5 and truncated HMACs | 7.0 notice | 7.2 | ripemd160 in 7.6 | Very old peers |
| RSA keys under 1024 bits | 7.0 notice | Refused in 7.6 | Refused | Ancient keys |
| Finite field DH in sshd | 10.0 | 10.0 server defaults | Still available | Clients that only do group exchange |
The legacy options page explains the symptom. Both sides must share at least one key exchange method, host key algorithm, cipher and MAC; otherwise, since 7.0, you see "Unable to negotiate with legacyhost: no matching key exchange method found" plus the other side's offer. The fix is to upgrade the other end or replace the weak key, with a "+" exception for that host only as a stopgap. A mismatch during user authentication looks like a wrong key instead: the page's example is an ssh-dss key in authorized_keys that sshd silently no longer accepts.
What do PerSourcePenalties and other connection limits do?
Before 9.8, sshd relied on MaxStartups, LoginGraceTime and MaxAuthTries. These limit load but do not remember who misbehaves. PerSourcePenalties, added in 9.8 and on by default, adds that memory. When an address repeatedly fails to authenticate, connects without authenticating or crashes a server process, sshd records a penalty. Once penalties pass a minimum, sshd refuses every connection from that address (and its block) until they expire, and repeat offences add up to a maximum. The 9.8 notes say the aim is to make password guessing and probing for sshd bugs much harder.
The current sshd_config manual page lists the defaults:
| Penalty | Applied when | Default |
|---|---|---|
| crash | A client causes an sshd process to crash | 90s |
| authfail | A client disconnects after failed authentication attempts | 5s |
| invaliduser | A client tries to log in as a user that does not exist (10.3) | 5s |
| refuseconnection | The RefuseConnection option rejected the client (9.9) | 10s |
| noauth | A client disconnects without trying to authenticate | 1s |
| grace-exceeded | A client fails to authenticate within LoginGraceTime | 10s |
| max | Most time a source can be refused | 10m |
| min | Penalty needed before enforcement starts | 15s |
PerSourceNetBlockSize (default 32:128, each address on its own) sets how addresses are grouped, PerSourcePenaltyExemptList exempts trusted ranges such as a bastion, and PerSourceMaxStartups (default none) caps unauthenticated connections per source.
The manual warns that the noauth penalty can catch legitimate scanners such as ssh-keyscan, and the 9.8 notes say servers with users behind NAT or proxies may need tuning, because one bad client can block a shared address. Behind a load balancer that hides client addresses, check what address sshd actually sees first.
How does the OpenSSH project disclose security issues?
At concept level, the process has three public pieces:
- Private reporting. The security page asks reporters to contact the private developers list at openssh@openssh.com and points to the OpenBSD security page for the wider process. The release notes repeat that address at the end of every release.
- Release notes with a Security section. Fixes ship in a normal release or a short point release, and the notes name the affected component (ssh, sshd, scp, sftp, ssh-agent), the affected version range, whether the issue applies to default configurations and who reported it. Portable only point releases such as 9.3p2 and 9.9p2 exist for fixes that do not need a new OpenBSD release.
- The security page as an index. It lists security relevant issues by date with affected versions and links. As fetched on 25 October 2026 it has 48 dated entries, the newest from 9 April 2025, so security fixes in 10.1 through 10.6 are documented in the release notes rather than that index. Read the notes for anything recent.
Release signing is also public. Each release lists SHA-1 and base64 encoded SHA-256 checksums, and the release notes link the PGP key at cdn.openbsd.org that signs the tarballs.
Since 2016 the project has shipped two to five releases a year, plus point releases when needed. 2026 has had four so far: 10.3, 10.4, 10.5 and 10.6.
For individual issues, our incident posts cover the details, for example regreSSHion explained. This post stays with the defaults.
How do distributions backport fixes, and how should you read a scanner finding?
Most servers do not run upstream OpenSSH. They run a distribution package built from an older upstream version with fixes backported. The Debian security FAQ explains the reasoning: instead of moving a stable release to a new upstream version, the security team backports fixes into the version that shipped, so that a security update "changes as little as possible". It also answers the question this causes, "The version number for a package indicates that I am still running a vulnerable version!", by telling you to compare the full package version with the one in the advisory.
That creates three different version numbers for one host:
| What you check | Example form | What it tells you |
|---|---|---|
| Upstream version in the SSH banner | SSH-2.0-OpenSSH_9.6p1 plus a distro suffix | Which upstream release the package started from |
ssh -V on the host | OpenSSH_9.6p1 plus distro text, OpenSSL version | The same, plus the crypto library |
| Full package version | dpkg -l openssh-server or rpm -q openssh-server | Which distribution fixes are applied |
A banner only scanner flags a fully patched distribution sshd as vulnerable and misses a hand built upstream OpenSSH that was never updated. When you triage a finding:
- Find out what produced it. Banner checks see only the first row of the table.
- Compare the host's package version with the distribution's advisory, not the upstream release notes.
- Check whether the issue needs a non-default option; many OpenSSH entries say the affected feature, such as VerifyHostKeyDNS, is off by default.
- Record the result ("backported in package version X, advisory Y") so nobody repeats the check.
And check your own tooling: a script matching "OpenSSH_1*" to find antique versions now matches 10.x.
Step by step: audit and upgrade OpenSSH safely
This walkthrough uses only OpenSSH's own commands plus your package manager. Run the server side commands on a host you can reach by a second route (a console or a second open session) in case a change locks you out.
1. Check client and server versions
# Client version and crypto library
ssh -V
# Server version (as root); sshd -V prints the version and exits
sudo sshd -V
# Package versions, which show backported fixes
dpkg -l openssh-client openssh-server # Debian, Ubuntu
rpm -q openssh openssh-server openssh-clients # RHEL family
# What a remote server announces (banner lines go to stdout since 9.8)
ssh-keyscan -t ed25519 server.example.com
The -V flag for sshd is documented in the current sshd manual page. On older builds that lack it, use the package version.
2. See the effective server configuration
sshd -T checks the configuration, applies any defaults and prints the result, which is what sshd will actually use, not what the file appears to say.
# Effective global configuration (10.4 switched the config dump to mixed case names, so grep with -i)
sudo sshd -T | sort
# Effective configuration for one connection, with Match blocks applied
sudo sshd -T -C user=deploy,host=build01.example.com,addr=10.0.0.15
# Just the algorithm lists
sudo sshd -T | grep -Ei '^(kexalgorithms|ciphers|macs|hostkeyalgorithms|pubkeyacceptedalgorithms) '
# Penalties and limits
sudo sshd -T | grep -Ei 'persource|maxstartups|logingracetime|maxauthtries'
Our post on sshd Match blocks and sshd -T covers the -C keywords in detail.
3. List the algorithms each side supports and uses
# What this ssh build supports (from the legacy options page)
ssh -Q kex
ssh -Q cipher
ssh -Q mac
ssh -Q key
# What the client will actually use for one host
ssh -G server.example.com | grep -Ei '^(kexalgorithms|ciphers|macs|hostkeyalgorithms) '
# Does the server still rely on ssh-rsa host signatures? (from the 8.2 notes)
ssh -oHostKeyAlgorithms=-ssh-rsa user@server.example.com
If the last command fails host key verification and the server has no other host key type, the 8.2 notes say the server software needs an upgrade.
4. Check post-quantum key exchange
With a 10.1 or later client, no warning means a post-quantum method was used. To confirm, check verbose output:
ssh -v user@server.example.com exit 2>&1 | grep -i 'kex: algorithm'
On servers running 10.6 or later, WarnWeakCrypto in sshd_config logs clients that do not use post-quantum key exchange, which gives you a list of clients to upgrade.
5. Review moduli if you still allow group exchange
Since 10.0, /etc/moduli matters only if you re-enabled group exchange. The moduli(5) manual page says the fifth field is the prime size in bits:
# Show how many groups exist at each size
awk '!/^#/ {print $5}' /etc/moduli | sort -n | uniq -c
# Keep only the larger groups (check the sizes printed above first)
sudo cp /etc/moduli /etc/moduli.bak
awk '!/^#/ && $5 >= 3071' /etc/moduli.bak | sudo tee /etc/moduli.new >/dev/null
sudo mv /etc/moduli.new /etc/moduli
sudo sshd -t
With no fallback to built in groups since 10.0, an over trimmed file breaks connections, so test with sshd -t and a new login before closing your session.
6. Build a legacy exception list
Write every exception down, scope it to one host or address and give it an owner and a removal date. On the client:
# ~/.ssh/config: one old switch that only signs with SHA-1 RSA
Host old-switch.example.com
HostKeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsa
# One server that cannot do post-quantum key exchange yet
Match host legacy-sftp.example.com
WarnWeakCrypto no-pq-kex
The "+" form appends to the defaults, so the host still gets the strongest shared algorithm as soon as it supports one. Never put these lines under Host *.
7. Stage the upgrade
- Inventory versions across the fleet, including container images and appliances.
- Read the "Potentially-incompatible changes" section of every release between your current and target versions. From 8.x to 10.x that includes 8.8, 9.0, 9.8, 10.0 and 10.1.
- Upgrade a canary group. Watch for "no matching" negotiation errors, sshd-session and sshd-auth names in log parsing, and penalty messages (logged at INFO since 10.1).
- Add PerSourcePenaltyExemptList entries for monitoring and management ranges.
- Roll out in waves, keeping a console path open until a fresh login works, then compare
sshd -Toutput with the canary to catch drift.
8. Plan host key rotation
UpdateHostKeys lets clients learn new host keys from a server they already trust, so you can add a new Ed25519 host key beside the old one and retire the old one later. Our guide on rotating SSH keys safely covers user keys.
Common mistakes
- Judging patch status by version string alone. Distribution packages backport fixes without changing the upstream number. Compare the full package version with the distribution advisory.
- Re-enabling ssh-rsa globally. One old device leads to
PubkeyAcceptedAlgorithms +ssh-rsaunderHost *or in sshd_config for everyone. Scope exceptions to a single host and give them an end date. - Confusing RSA keys with ssh-rsa signatures. Replacing every RSA key after 8.8 is unnecessary; the same key signs with rsa-sha2-256 or rsa-sha2-512.
- Leaving old moduli and group exchange enabled. If you re-enabled finite field Diffie-Hellman after 10.0, review /etc/moduli. If you did not, remove leftover KexAlgorithms lines that re-add those methods.
- No host key rotation plan. Host keys from installs a decade ago are often still RSA or even DSA. After 10.0 a DSA host key does nothing, and clients may already be using another key type without anyone noticing.
- Silencing the post-quantum warning everywhere.
WarnWeakCrypto noin a global block hides the one signal that tells you which servers still need upgrading. UseMatch hostexceptions. - Ignoring the new process names. Log alerts that look only for "sshd" miss messages from sshd-session and sshd-auth.
- Applying penalties without exemptions. A misconfigured monitoring probe or a NAT gateway can lock out legitimate traffic.
FAQ
When was OpenSSH first released?
OpenSSH 1.2.2 shipped with OpenBSD 2.6 on 1 December 1999. The code was imported into OpenBSD on 26 September 1999, forked from Björn Grönvall's OSSH, which was built on Tatu Ylönen's ssh 1.2.12, according to the OpenSSH history page.
What is the latest version of OpenSSH?
As of 25 October 2026 the latest release is OpenSSH 10.6, released on 6 October 2026, with the portable version 10.6p1. OpenBSD 8.0, released on 20 October 2026, ships it.
When did OpenSSH drop SSH protocol 1?
Protocol 1 was disabled by default in 5.4 (March 2010) and at compile time in 7.0 (August 2015). Server support was removed in 7.4 (December 2016) and the client code was deleted in 7.6 (October 2017).
Does OpenSSH 8.8 or later still support RSA keys?
Yes. 8.8 disabled RSA signatures that use SHA-1, the "ssh-rsa" algorithm, not RSA keys. Existing RSA keys sign with rsa-sha2-256 or rsa-sha2-512, supported since 7.2. ssh-keygen has generated Ed25519 keys by default since 9.5.
When was DSA removed from OpenSSH?
DSA was disabled at run time in 7.0 (2015), made compile-time optional in 9.7, disabled at compile time in 9.8 and removed entirely in 10.0 (9 April 2025).
Is OpenSSH post-quantum secure?
Its key exchange is post-quantum by default: sntrup761x25519-sha512 since 9.0 (April 2022) and mlkem768x25519-sha256 since 10.0 (April 2025). Both sides must support one of them. Post-quantum signatures are newer: an ML-DSA 44 and Ed25519 composite was added experimentally in 10.4 and enabled under its IANA name in 10.6.
What is PerSourcePenalties in sshd_config?
It is an sshd feature, added in 9.8 and on by default, that temporarily refuses connections from addresses that repeatedly fail authentication, connect without authenticating or crash sshd. Penalties add up to a maximum of 10 minutes by default, and PerSourcePenaltyExemptList exempts trusted ranges.
Why does a scanner say my OpenSSH is vulnerable after I patched?
Usually because it reads the upstream version from the SSH banner, and your distribution backported the fix without changing that number. Check the full package version against your distribution's advisory and record the result as an exception if the fix is present.
Why supply chain hygiene still matters here
OpenSSH on a host usually comes from your operating system vendor, so its fixes arrive through distribution updates, not through anything you build. But SSH clients, SSH libraries and OpenSSH packages also end up inside container images, CI runners and the applications you ship, and those copies age just like any other open source dependency.
Fix it without waiting for upstream
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation categories include dependencies, licenses, security posture and findings, and each one is set separately. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds.
3. Gold Registry: start from components that are already fixed
Gold Registry provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs and a completed license check, with signed build provenance (signed with Safeguard's key and verifiable with cosign, SLSA Build Level 3 as the design target) and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. Ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | Safeguard policies and gates in CI |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
Safeguard does not configure sshd, rotate host keys or patch the OpenSSH package on a server you run; those come from your distribution and your own change process. The fit is with the images and repositories you build, where SSH clients, SSH libraries and OpenSSH packages travel along with everything else.
The first demo sets up the local runner on your own machine, approves it with its device code, then scans from the terminal. If your build hosts produce container images that install an SSH client or server package, this is how you scan those images and repositories where they live and see which versions they carry.
Click through it at your own pace, or open it full screen.
The second demo is a real Safeguard CLI session. Asked to fix a lodash CVE, the assistant reads the finding and the advisory, asks before any command that changes something, edits package.json to lodash 4.17.21, runs npm install and npm test with your approval, prints a summary and pushes nothing. The same flow applies when a deployment tool or SSH library in your own repository needs a version bump that you want reviewed and tested before it lands.
Click through it at your own pace, or open it full screen.
The third demo connects Claude to the Safeguard MCP server with OAuth and chooses which tools Claude can use. Once your projects are in Safeguard, you can ask which of them contain a given component and version, such as images still built with an SSH package older than the version your distribution's advisory names.
Click through it at your own pace, or open it full screen.
To try the CLI, download Safeguard and follow the CLI documentation.
Quick check
A client running OpenSSH 10.1 prints a post-quantum warning when connecting to a server. What is the first thing to check on the server?
Its version and its KexAlgorithms setting. Servers from 9.0 support sntrup761x25519-sha512 and from 9.9 support mlkem768x25519-sha256. If the server is new enough, run sshd -T and check whether KexAlgorithms removed both. Only silence the warning with a per host Match host block if you accept the risk.
After upgrading to 8.8, logins to an old switch fail. Do you need to replace your RSA keys?
No. 8.8 disabled RSA signatures with SHA-1 (ssh-rsa), not RSA keys. The switch probably cannot verify rsa-sha2 signatures. Add a scoped Host exception with +ssh-rsa for that switch only, and plan to upgrade or replace it.
Sources
- OpenSSH release notes (all releases, 1.2.2p1 to 10.6)
- OpenSSH project history
- OpenSSH security page
- OpenSSH legacy options
- OpenSSH post-quantum cryptography
- OpenSSH portable release
- openssh-portable repository
- OpenBSD 2.6 release page
- OpenBSD 2.7 release page
- OpenBSD 7.8 release page
- OpenBSD 7.9 release page
- openbsd-announce: OpenBSD 8.0 released Oct 20, 2026
- openssh-unix-announce: OpenSSH 10.6 released
- OpenBSD security page
- sshd_config(5) manual page
- ssh_config(5) manual page
- sshd(8) manual page
- moduli(5) manual page
- Debian security FAQ
Last reviewed: 25 October 2026.