~/blog/downgrade-attack-tls-version-enforcementPOSTED
von Shady Nathan Tawfik · 15. August 2026 · 3 min

Wenn TLS-Durchsetzung optional ist, werden Downgrades langweilig

Eine Universitäts--Content-Plattform lieferte auf manchen Hosts gemischte Protokollversionen. Kein Cipher war schwach, kein Zertifikat abgelaufen, und die Transportschicht fiel trotzdem durch die Bewertung.

Interaktiv[blog-2.0]

TL;DR: Kein schwacher Cipher, kein abgelaufenes Zertifikat — und die Transportschicht fiel trotzdem durch. Ich habe die Protokollerzwingung pro Hostnamen geprüft, den Archiv-Host mit älterer Aushandlung gefunden und eine estate-weite Basislinie gesetzt.

Ausgangslage

Ich habe das Ziel als Black-Box geprüft: keine Zugangsdaten, nur die öffentlich sichtbare Fläche. Das ist, was ich sah, bevor ich etwas angefasst habe.

Eine Universitäts--Content-Plattform auf einem Enterprise-Content-System mit campusweitem Servicekatalog, verteilt über mehrere Hosts einschliesslich eines Archivs. Geprüft wurden die TLS-Konfiguration jedes Hosts, E-Mail-Authentifizierung, Header-Policy und interne Host-Freigabe.

Ich habe alle identifizierenden Informationen entfernt. Das Ziel wird ausschließlich nach Branche und Technologieklasse beschrieben, damit das Muster übertragbar bleibt.

Das Muster

Das Muster, das ich erkannt habe — und das ich in ähnlichen Landschaften immer wieder finde:

Transportsicherheit ist die einzige Schicht, in der ein Downgrade im Screenshot unsichtbar bleibt. Eine Protokoll-Aushandlung, die auf eine ältere Version zurückfällt, gelingt, das Schlösschen erscheint, das Zertifikat ist gültig, die Seite lädt. Der Nutzer hat kein Signal.

Die Ursache ist fast immer der Konfigurationsbereich. Ein Administrator sichert den Haupthost, prüft ihn und hört auf. Archiv-Hosts, Vorschaumgebungen und Service-Subdomains erben die Plattformvorgaben, und die sind permissiv.

Befundrelevant wird das in Kombination mit allem anderen. Eine Seite, die auf zwei Hosts erzwingt und auf drei nicht, hat eine inkonsistente Perimeter, und eine inkonsistente Perimeter ist dort, wo ein Angreifer sich stellt.

TL;DR: Eine Universitäts--Content-Plattform lieferte auf manchen Hosts gemischte Protokollversionen. Kein Cipher war schwach, kein Zertifikat abgelaufen, und die Transportschicht fiel trotzdem durch die Bewertung.

Befunde

SchweregradBefundNachweis
MITTELÄltere Transportversionen auf Archiv-Hosts akzeptiertDowngrade auf Hosts möglich, den der Haupthost nicht erlaubt
MITTELE-Mail-Authentifizisierung für die Absenderdomain nicht erzwungenUniversitäts--Mail fälschbar
MITTELInterne Hostnamen in Response-Headern exponiertServicebenennung im Internet sichtbar
NIEDRIGProtokollerzwingung zwischen Hosts inkonsistentKeine estate-weite Basislinie
NIEDRIGStrict Transport Security ohne PreloadPolicy vorhanden, aber nicht zur Preload eingereicht
INFOZertifikate gültig und Schlüsselgrößen angemessenKein Befund auf Zertifikatsebene

Der Angriffspfad

Client offers modern and older protocolsPrimary host negotiates modernArchive host negotiates olderDowngrade to a weaker suiteInconsistent perimeter across the estate

Reproduktion im Labor

Jeder Befehl unten zielt auf einen Lab-Container, den ich selbst kontrolliere. Nichts davon ist auf ein laufendes System gerichtet.

Protokollerzwingung pro Host in der eigenen Landschaft prüfen

~/code/bash bash
# jeden Host prüfen, nicht nur den Haupthost
$ for h in example.org www.example.org archive.example.org; do
    printf '%-26s ' "$h"
    curl -so /dev/null -w '%{http_version} %{ssl_verify_result}\n' --tlsv1.0 --tls-max 1.0 "https://$h/" 2>&1 | tail -1
  done

# und was der strengste Host tatsächlich ausgehandelt hat
$ curl -sv https://example.org/ 2>&1 | grep -iE 'SSL connection using|TLSv1'

Erkennung in eigener Infrastruktur

Jeden Hostnamen aufzählen, der Inhalte ausliefert

~/code/bash bash
$ for h in $(cat hosts.txt); do
    printf '%s ' "$h"
    openssl s_client -connect "$h:443" -servername "$h" </dev/null 2>/dev/null \
      | openssl x509 -noout -subject -dates 2>/dev/null | tr '\n' ' '
    echo
done

Behebung

Eine Transport-Baseline für die gesamte Landschaft setzen

~/code/nginx nginx
# estate-weite Basislinie: nur moderne Versionen, nur Forward Secrecy
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;

# überall anwenden, auch auf Archiv- und Vorschaum-Hosts
include /etc/nginx/tls-baseline.conf;

Sobald ein Angreifer Zugriff hat, kann er folgende Aktionen durchführen:

Ältere Transportversionen auf Archiv-Hosts akzeptiert
E-Mail-Authentifizisierung für die Absenderdomain nicht erzwungen
Interne Hostnamen in Response-Headern exponiert

Fazit — was ich daraus mitnehme

  • Prüfen Sie die Transportkonfiguration pro Hostnamen; ein Screenshot beweist nichts über die Aushandlung.
  • Archiv- und Vorschaum-Hosts sind diejenigen, die die Basislinie verfehlen.
  • Eine inkonsistente Perimeter ist dort, wo ein Angreifer sich stellt.
  • Preload erfordert eine ausdrückliche Einreichung; der Header allein bewirkt nichts zusätzlich.

Nur zu Bildungszwecken. Alle reproduzierbaren Befehle zielen auf meine eigene Lab-Umgebung (localhost), nie auf ein laufendes System. © 2026