A handshake that ends in an error feels like an attack until you look at the message, and most of the time it is arithmetic: no shared proposal, a certificate that expired, or a packet that never arrived. A downgrade attack is a specific thing, an adversary pushing both ends toward weaker behaviour than they would have chosen, and it leaves different evidence. Treating every failure as malice sends you looking for a victim that may not exist, while treating a real downgrade as a config bug leaves the weaker result in place. The two errors cost different things, and only one of them is cheap to correct.

What a downgrade attack actually changes

The goal is not to break the cipher and not to guess a key. It is to influence the negotiation so that both sides settle on an older version or a weaker algorithm that the attacker can handle, or to make one side believe the other only supports that.

What changes is the agreement, not the authentication. If the attacker can alter the offer without being detected, the peers may still complete a handshake and still authenticate each other, using parameters the attacker prefers. That is why the interesting question is whether the negotiation itself is covered by integrity protection.

Why the negotiation itself has to be authenticated

TLS 1.3 makes that protection explicit. A client that offered the newer version can detect a server that silently fell back, because the server's random value carries a fixed pattern when it negotiates 1.2 or lower, and the client is required to abort on seeing it. The full transcript is also covered by the finished messages, so an edit anywhere in the negotiation fails later rather than passing quietly.

Older protocol versions had weaker stories, which is exactly why the fallback rules matter. A TLS 1.0 endpoint in the same configuration is not a downgrade attack; it is a permitted outcome that you chose to allow. The distinction is whether the weaker result came from a protected decision or from an unprotected path an intermediary could influence. Once you state it that way, most incidents resolve into a configuration question.

IKEv2 behaves differently but rests on the same idea. The proposal is carried in an exchange that later authenticates both peers, so a middlebox that rewrites the algorithms breaks the authentication instead of steering it. OpenVPN's control channel protects its negotiation with TLS and then uses that channel to agree the data-channel ciphers, which is why the control-channel log line is the interesting one.

Configured compatibility is not an attack

An administrator can deliberately permit older choices to support a documented client, and that decision is visible in the configuration. An attacker cannot create that configuration; they can only exploit a path that lets them influence the offer.

The dangerous repair is the one that removes evidence of the difference. Enabling every legacy option, disabling certificate verification or accepting any cipher to make an error disappear broadens what the endpoint will agree to, and it removes the checks that would have reported the weaker result.

If you must keep one old option, name the reason and the client in the configuration comment, then limit the option to the profile that needs it. A compatibility exception scoped to one listener is auditable; one applied globally is just a weaker default nobody remembers choosing.

Read the failure before naming a cause

Preserve the exact error string, the client and server versions and the time before any change. Then classify: did the peers disagree about a proposal, did the packet path fail, or did one side reject a parameter it once accepted?

A proposal mismatch usually names the offending versions or algorithms and fails at the same point every time. A routing or MTU problem stalls or times out, and it often varies with network. An expired certificate produces a verification error rather than a negotiation one.

None of those is evidence of an attacker. Assigning malicious cause on the first failure is the mistake, because it hides the deterministic bug that will fail again tomorrow.

Confirm what was actually selected

After any correction, read the negotiated result instead of trusting that the connection came up. A connection that succeeds because verification was removed is the outcome you were trying to avoid. The two commands that follow show the chosen version and cipher for a TLS-fronted endpoint, plus the control-channel line from an OpenVPN server log.

openssl s_client -connect 198.51.100.30:443 -servername vpn.example.net -brief </dev/null
grep -iE 'Control Channel|Data Channel' /var/log/openvpn.log | tail -5

Compare the result with the configuration you intended, not with whatever now connects. If the selected version is older than the allowed minimum, the profile or the fallback list is where to look, and the answer is to fix that list rather than to declare an attack and rebuild everything around it.

Pennadoù liammet