HSTS - HTTP Strict Transport Security
HTTP Strict Transport Security (HSTS) ist ein HTTP-Response-Header der Browser anweist eine Domain ausschliesslich über HTTPS zu erreichen. Nach dem ersten HTTPS-Kontakt werden alle weiteren HTTP-Verbindungen clientseitig zu HTTPS umgeleitet bevor sie den Server erreichen. Dies verhindert SSL-Stripping-Angriffe und verhindert dass Nutzer versehentlich unverschlüsselte Verbindungen aufbauen. Wichtige Parameter: max-age, includeSubDomains, preload.
HTTP Strict Transport Security (HSTS) ist ein Sicherheitsmechanismus der über den HTTP-Response-Header Strict-Transport-Security implementiert wird. Er weist Browser an, eine Domain in Zukunft ausschließlich über HTTPS zu kontaktieren - auch wenn der Nutzer http:// eingibt oder einem HTTP-Link folgt. Damit schließt HSTS die Lücke die klassische HTTPS-Redirects offen lassen.
Das Problem das HSTS löst
SSL-Stripping ohne HSTS:
Normaler HTTPS-Redirect (anfällig!):
1. Nutzer tippt: http://bank.com (HTTP!)
2. Bank-Server: 301 Redirect → https://bank.com
3. Browser: folgt Redirect → HTTPS-Verbindung
SSL-Stripping-Angriff (MITM im selben Netzwerk):
1. Nutzer tippt: http://bank.com (HTTP!)
2. Angreifer (zwischen Nutzer und Bank, z.B. öffentliches WLAN):
→ Interceptiert die HTTP-Anfrage
→ Baut selbst HTTPS-Verbindung zu bank.com auf
→ Liefert Seite als HTTP an den Nutzer (kein Schloss im Browser!)
3. Nutzer sieht: kein Schloss → denkt: "Okay, HTTP halt"
4. Angreifer liest alle Daten im Klartext!
→ Problem: Erste HTTP-Anfrage kommt NIE beim echten Server an
Mit HSTS (Schutz nach erstem Besuch):
1. Erster Besuch (einmalig über HTTPS):
Response-Header: Strict-Transport-Security: max-age=31536000
→ Browser merkt sich: "bank.com = nur HTTPS für 1 Jahr!"
2. Nächste Anfrage (HTTP oder HTTPS):
→ Browser macht INTERN https://bank.com (ohne HTTP-Anfrage!)
→ Server sieht nur HTTPS-Anfragen
→ SSL-Stripping schlägt fehl: HTTP-Anfrage verlässt Browser nicht!
Einschränkung: Erster Besuch noch anfällig!
→ Lösung: HSTS Preload List (Browser-intern)
HSTS-Header Syntax
Strict-Transport-Security Header:
Basis:
Strict-Transport-Security: max-age=31536000
Mit Subdomains:
Strict-Transport-Security: max-age=31536000; includeSubDomains
Mit Preload:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Parameter-Bedeutung:
max-age=<seconds>:
→ Wie lange Browser HSTS für diese Domain merkt (in Sekunden)
→ 31536000 = 1 Jahr (empfohlen für Produktion)
→ 86400 = 1 Tag (für Testing)
→ Bei jedem Response erneuert sich der Timer!
→ max-age=0 → HSTS löschen (für Deaktivierung)
includeSubDomains:
→ HSTS gilt auch für alle Subdomains
→ VORSICHT: alle Subdomains müssen HTTPS unterstützen!
→ Sonst: *.example.com ohne HTTPS → sofort unzugänglich!
→ Testen bevor aktivieren: alle Subdomains via HTTPS erreichbar?
preload:
→ Markiert Domain für Aufnahme in Browser Preload List
→ Preload List ist in Browser-Binaries einkompiliert
→ Schützt bereits beim ERSTEN Besuch (kein erster HTTP-Call!)
→ Einmalig für Domain-Aufnahme: hstspreload.org
→ SEHR SCHWER RÜCKGÄNGIG ZU MACHEN! (Browser-Updates dauern Monate)
NICHT auf Development/Test-Domains setzen:
→ max-age > 0 auf localhost → Browser kann localhost nicht mehr per HTTP erreichen!
→ Immer nur auf Produktion + entsprechend langen max-age
Implementierung
Server-Konfigurationen:
Nginx:
server {
listen 443 ssl http2;
server_name example.com;
# HSTS Header (ERST nach vollständiger HTTPS-Einrichtung!):
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# HTTP → HTTPS Redirect (zusätzlich zu HSTS):
# (für den allerersten Besuch vor HSTS-Caching)
}
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
Apache:
<VirtualHost *:443>
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
</VirtualHost>
# HTTP → HTTPS
<VirtualHost *:80>
Redirect permanent / https://example.com/
</VirtualHost>
Caddy (automatisch!):
example.com {
# Caddy aktiviert HTTPS + HTTP-Redirect automatisch!
# HSTS-Header automatisch gesetzt wenn Caddy HTTPS verwaltet
}
Node.js (Express):
const helmet = require('helmet');
app.use(helmet.hsts({
maxAge: 31536000,
includeSubDomains: true,
preload: true
}));
Python (Django):
# settings.py:
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_SSL_REDIRECT = True # HTTP → HTTPS Redirect
Schrittweise Einführung:
Phase 1: max-age=86400 (1 Tag) - testen ob alles funktioniert
Phase 2: max-age=2592000 (1 Monat) - längere Beobachtung
Phase 3: max-age=31536000 + includeSubDomains - Produktion
Phase 4: preload beantragen - wenn sicher
HSTS Preload List
Browser Preload List Prozess:
Was ist die Preload List?
→ Liste von Domains die Browser von Anfang an als HTTPS-only kennen
→ Eingebaut in: Chrome, Firefox, Safari, Edge, Opera
→ Schützt bereits beim ALLERERSTEN Besuch!
→ Selbst wenn eigener Server kompromittiert → Browser lehnt HTTP ab
Aufnahme in Preload List:
Voraussetzungen (alle müssen erfüllt sein!):
□ Gültiges HTTPS-Zertifikat auf der Domain
□ HTTP-Redirect zu HTTPS (301 oder 302)
□ HSTS-Header mit:
- max-age >= 31536000 (1 Jahr!)
- includeSubDomains
- preload
□ ALLE Subdomains über HTTPS erreichbar!
Beantragen: hstspreload.org
→ Domain eingeben → Status prüfen
→ Antrag einreichen → Chrome-Chromium-Repository PR
→ Review: 1-3 Monate bis in aktiven Browser-Versionen
Präload-Liste aktualisieren (ACHTUNG!):
→ Entfernung aus der Liste dauert Monate bis Jahre
→ Browser-Releases sind nicht sofort überall
→ Ältere Browser-Versionen behalten alten Stand
→ Deshalb: preload ist PERMANENT (praktisch gesehen!)
Domains die preload verwenden:
Alle .gov und .mil domains: preloaded!
Google, Facebook, Twitter: preloaded
Immer mehr DE-Behörden: preloaded
Alternativer Schutz ohne preload:
→ HTTPS-Only-Cookie: Secure; SameSite=Strict
→ Certificate Transparency Monitoring
→ DNS CAA-Records: nur autorisierte CAs dürfen Zertifikate ausstellen
Testing und Probleme
HSTS testen:
1. Header prüfen:
curl -s -D - https://example.com -o /dev/null | grep -i strict-transport
→ Strict-Transport-Security: max-age=31536000; includeSubDomains
2. HSTS Preload Status:
hstspreload.org → Domain eingeben → Status
3. SSL Labs (umfassend):
ssllabs.com/ssltest/ → prüft TLS + HSTS + Preload-Status
4. Security Headers:
securityheaders.com → prüft alle HTTP-Security-Header
Häufige HSTS-Probleme:
Problem 1: Mixed Content nach HSTS-Aktivierung:
HSTS → alle Subdomains müssen HTTPS haben
alt.example.com noch auf HTTP → Subpages unzugänglich!
Lösung: alle Subdomains vor includeSubDomains auf HTTPS umstellen
Problem 2: HSTS auf localhost/Staging:
max-age zu lang auf localhost → Browser versucht localhost via HTTPS
→ Entwicklungsumgebungen nicht erreichbar!
Lösung: HSTS NUR auf Produktions-URLs setzen
Unterschiedliche Nginx-Configs für Dev/Prod
Problem 3: Zertifikat läuft ab:
HSTS aktiv + Zertifikat abgelaufen → Seite KOMPLETT unzugänglich!
Keine manuelle HTTP-Ausnahme möglich bei HSTS
Lösung: Certificate-Monitoring + Auto-Renewal (Let's Encrypt)
Problem 4: max-age=0 zum Deaktivieren:
Strict-Transport-Security: max-age=0
→ Browser löscht HSTS für diese Domain
→ Kann Monate dauern bis alle Browser-Caches geleert sind