In diesem Lab erstellst du ein selbstsigniertes Zertifikat mit openssl, konfigurierst nginx für HTTPS, inspizierst die Verbindung mit openssl und sslscan, und härtest die TLS-Konfiguration.
Nach diesem Lab kannst du:
Wir setzen direkt am vorangegangenen Praxis-Lab an und gehen davon aus, dass du einen installierten nginx-Server auf Kali Linux am Start hast.
Ein TLS-Zertifikat ist ein digitales Dokument, das zwei Dinge erfüllt:
CN=patientenportal.novahealth-soltuions.de).Bei öffentlichen Zertifizierungsstellen wie Let’s Encrypt ist diese CA öffentlich vertrauenswürdig. Bei einem self-signed certificate ist der Aussteller identisch mit dem Empfänger, das heißt: das Zertifikat wurde vom Server selbst signiert. Der Browser findet es in keiner seiner Vertrauenslisten (Truststore) und zeigt deshalb eine Warnung an.
💡 Browser benötigen immer einen Truststore mit vertrauenswürdigen CA-Zertifikaten. Je nach Browser wird dafür ein eigener Zertifikatsspeicher verwendet oder auf Vertrauensinformationen des Betriebssystems zurückgegriffen. Wenn du Windows verwendest, kannst du die hinterlegten CA-Zertifikate im Zertifikatsmanager einsehen. Du kannst die Management-Konsole aufrufen durch Eingabe von certmgr.msc im Ausführen-Dialog (Windows+R). Bekannte Namen sind z.B. DigiCert, GlobalSign, ISRG (Let’s Encrypt) oder Sectigo.
Firefox verwendet traditionell einen eigenen, auf NSS basierenden Zertifikatsspeicher, kann aber auch Zertifikate aus dem Betriebssystem berücksichtigen. Die nachfolgende Abbildung zeigt den Firefox-eigenen Zertifikatsspeicher:

So, jetzt wird es anspruchsvoller, denn kein normalsterblicher Mensch kann sich openssl-Kommandos merken. Trotzdem brauchen wir das Tool, um die Zertifikate korrekt zu generieren. Zuerst erstellst du einen geeigneten Ordner dafür:
sudo mkdir -p /etc/nginx/ssl
Erstelle jetzt das Zertifikat und den privaten Schlüssel in einem einzigen Schritt:
sudo openssl req -x509 \
-newkey rsa:4096 \
-keyout /etc/nginx/ssl/novahealth.key \
-out /etc/nginx/ssl/novahealth.crt \
-sha256 \
-days 365 \
-nodes \
-subj "/CN=localhost/O=NovaHealth Solutions AG/C=DE" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
Was bedeuten die Parameter?
| Parameter | Bedeutung |
|---|---|
req -x509 | Erstellt direkt ein selbstsigniertes Zertifikat (kein CSR an eine CA) |
-newkey rsa:4096 | Generiert gleichzeitig einen neuen RSA-Schlüssel mit 4096 Bit |
-keyout | Speicherpfad für den privaten Schlüssel |
-out | Speicherpfad für das Zertifikat |
-sha256 | SHA-256 als Signatur-Hash-Algorithmus |
-days 365 | Zertifikat ist 365 Tage gültig |
-nodes | No DES: privater Schlüssel wird nicht mit einem Passwort verschlüsselt |
-subj | Zertifikatsfelder ohne interaktiven Dialog setzen |
-addext subjectAltName | Subject Alternative Names (seit 2017 von Browsern zwingend verlangt) |
⚠️ Zur Option -nodes: Ohne diese Option würde nginx bei jedem Start nach dem Passwort für den privaten Schlüssel fragen und in einer automatisierten Serverumgebung ist das nicht praktikabel. In der Produktion ist der private Schlüssel durch Dateisystemberechtigungen des Servers geschützt (Besitzer: root, Rechte: 600).
Falls du dich gewundert hast: In unserem Lab erstellen wir das Zertifikat für localhost. In der Praxis muss hier der DNS-Name stehen. Da wir an dieser Stelle jedoch auf die DNS-Thematik nicht weiter eingehen wollen, nutzen wir localhost, da dieser Name intern automatisch auf 127.0.0.1 aufgelöst wird.
Setze nun noch die korrekten Berechtigungen:
sudo chmod 600 /etc/nginx/ssl/novahealth.key
sudo chmod 644 /etc/nginx/ssl/novahealth.crt
Anschließend kannst du das Zertifikat mit folgendem Kommando anschauen: openssl x509 -in /etc/nginx/ssl/novahealth.crt -text -noout. Heraus kommen sollte in etwa Folgendes (gekürzt):
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
63:e1:c8:23:a3:71:c9:1d:0f:f3:fe:ee:85:0d:66:8c:4d:46:42:76
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=localhost, O=NovaHealth Solutions AG, C=DE
Validity
Not Before: Jun 6 15:43:08 2026 GMT
Not After : Jun 6 15:43:08 2027 GMT
Subject: CN=localhost, O=NovaHealth Solutions AG, C=DE
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (4096 bit)
Modulus:
00:d3:ee:2a:f1:ca:91:76:76:cc:5e:27:be:b1:24:
---- Kürzung ----
47:d1:21:ad:d6:9b:a9:bd:10:0b:65:fc:6e:a8:8d:
65:65:ff
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
D8:8C:85:69:6C:45:8B:55:92:7D:E7:7F:C0:8E:7A:36:53:C6:29:B3
X509v3 Authority Key Identifier:
D8:8C:85:69:6C:45:8B:55:92:7D:E7:7F:C0:8E:7A:36:53:C6:29:B3
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Subject Alternative Name:
DNS:localhost, IP Address:127.0.0.1
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
08:f8:2c:fd:f4:e2:d8:5d:89:5d:48:bb:30:59:10:51:47:8e:
---- Kürzung ----
ea:30:e9:17:3c:c9:c5:a9
💡 Achtung: Issuer und Subject sind identisch. Das ist das Erkennungsmerkmal eines self-signed Certificates. Bei einem Let’s Encrypt-Zertifikat stünde als Issuer z.B. CN=R11, O=Let's Encrypt, C=US, denn da hätte die vertrauenswürdige CA das ausgestellte Zertifikat signiert und sagt somit, dass der Webserver auch vertrauenswürdig ist.
Öffne die Default-Site von nginx mit dem Editor:
sudo nano /etc/nginx/sites-available/default
Nun musst du nginx sagen:
In der Standard-Konfiguration stehen im server { }-Block nur die folgenden beiden aktiven Direktiven:
listen 80 default_server;
listen [::]:80 default_server;
Direkt danach kannst du nun return 301 https://$host$request_uri; einfügen, um die Weiterleitung auf HTTPS zu aktivieren:

Unter dem ersten server{}-Block, der mit der schließenden geschweiften Klammer } abgeschlossen wird, muss jetzt ein weiterer Block mit der TLS Konfiguration eingefügt werden:
# HTTPS-Server
server {
listen 443 ssl;
server_name localhost;
# Zertifikat und privater Schlüssel
ssl_certificate /etc/nginx/ssl/novahealth.crt;
ssl_certificate_key /etc/nginx/ssl/novahealth.key;
# TLS-Protokolle (vorerst minimal)
ssl_protocols TLSv1.2 TLSv1.3;
# Webroot
root /var/www/html;
index index.html;
}
Anschließend kannst du die Syntax prüfen und die Konfiguration neu laden:
sudo nginx -t && sudo systemctl reload nginx
Auf dem System sollte jetzt Port 443 offen sein. Das kannst du mit dem Kommando sudo ss -tlpn prüfen:

Teste zuerst, ob der HTTP-Redirect funktioniert:
curl -I http://localhost
Die Rückgabe des Servers ist ein 301 Moved Permanently und ein Header Location, der auf die HTTPS-URL verweist:

Teste jetzt mit curl, ob HTTPS funktioniert. Da das Zertifikat self-signed (und somit nicht vertrauenswürdig) ist, musst du die Zertifikatsvalidierung mit -k (insecure) deaktivieren:
curl -k -I https://localhost
Das Ergebnis sollte sich darstellen, wie in der nachfolgenden Abbildung gezeigt:

Gibst du im Browser https://localhost ein, erscheint eine Zertifikatswarnmeldung:

Auch mit openssl sollte nun eine Verbindung möglich sein, die die Details des Zertifikats anzeigt. Der Befehl lautet: openssl s_client -connect localhost:443 -brief. Die Ausgabe stellt sich folgendermaßen dar:

Gehen wir die wichtigsten Zeilen noch einmal kurz durch:
Protocol version: TLSv1.3 = die Verbindung nutzt das aktuelle TLS-ProtokollVerification error: num=18:self signed certificate = openssl bestätigt das Problem: keine CA-SignaturCiphersuite: TLS_AES_256_GCM_SHA384 = bei TLS 1.3 wählt der Standard automatisch sichere SuitesLast but not least können wir die TLS-Konfiguration mit einigen Parametern noch etwas sicherer machen. Öffne die Default-Site-Konfiguration erneut mit dem Editor:
sudo nano /etc/nginx/sites-available/default
Erweitere die nginx HTTPS-Konfiguration im zweiten server{}-Block folgendermaßen:
server {
listen 443 ssl;
server_name localhost;
ssl_certificate /etc/nginx/ssl/novahealth.crt;
ssl_certificate_key /etc/nginx/ssl/novahealth.key;
# Nur TLS 1.2 und 1.3 erlauben (Whitelist-Ansatz)
ssl_protocols TLSv1.2 TLSv1.3;
# Nur sichere TLS 1.2 Cipher Suites erlauben
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
# Browser-seitige Cipher-Wahl (empfohlen für TLS 1.3)
ssl_prefer_server_ciphers off;
# Session Tickets deaktivieren (Forward Secrecy schützen)
ssl_session_tickets off;
# Session-Cache für Performance
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# Webroot
root /var/www/html;
index index.html;
}
Die Bedeutung der meisten Parameter kennst du oder kannst dir durch die Kommentare einfach erschließen. Insbesondere zwei neue Parameter erläutern wir im Folgenden:
Die Konfiguration ssl_prefer_server_ciphers off; beantwortet die Frage: Wenn Client und Server mehrere gemeinsame Cipher Suites unterstützen, wessen Präferenz soll bei der Auswahl berücksichtigt werden? Mit off überlässt Nginx die Präferenz dem Client. Das kann sinnvoll sein, weil moderne Clients häufig besser beurteilen können, welche der angebotenen Verfahren auf ihrer Hardware besonders effizient arbeiten.
Voraussetzung ist natürlich, dass der Server ausschließlich sichere Cipher Suites zulässt. Bei TLS 1.3 stehen ohnehin nur moderne AEAD-Verfahren zur Verfügung. Aus Sicherheitssicht ist es daher in der Regel unproblematisch, ob beispielsweise AES-GCM oder ChaCha20-Poly1305 verwendet wird. Der Client kann dann das für ihn geeignetere Verfahren bevorzugen.
📌 Hinweis: ssl_prefer_server_ciphers betrifft die Cipher-Auswahl bei TLS 1.2 und älter. Auf die Auswahl der Cipher Suites von TLS 1.3 hat diese Direktive keinen entsprechenden Einfluss.
Die Konfiguration ssl_session_tickets off; deaktiviert TLS Session Tickets. Session Tickets ermöglichen es einem Client, eine frühere TLS-Sitzung effizient wiederaufzunehmen, anstatt bei jeder Verbindung eine vollständig neue Sitzung auszuhandeln. Dazu erhält der Client vom Server ein Ticket, das Informationen enthält, mit denen der Server die Sitzung später wiederaufnehmen kann.
Session Tickets können bei ungeeigneter Schlüsselverwaltung die Forward Secrecy schwächen. Forward Secrecy sorgt dafür, dass die spätere Kompromittierung langfristiger Schlüssel nicht dazu führt, dass zuvor aufgezeichnete TLS-Verbindungen nachträglich entschlüsselt werden können.
Werden jedoch die Schlüssel, mit denen Session Tickets geschützt werden, über einen längeren Zeitraum verwendet und später kompromittiert, kann dadurch die Vertraulichkeit der zugehörigen wiederaufgenommenen Sitzungen gefährdet werden. Das Deaktivieren von Session Tickets vermeidet dieses zusätzliche Schlüsselmanagement, verzichtet dafür aber auf die damit verbundenen Performance-Vorteile bei der Wiederaufnahme von TLS-Sitzungen.
Prüfe zunächst, ob die neue Konfiguration fehlerfrei ist mit dem bereits bekannten Befehl:
sudo nginx -t
Werden keine Fehler erkannt, kannst du die Konfiguration neu einlesen:
sudo systemctl reload nginx
Nun kannst du die neue Konfiguration testen. Dafür stehen dir verschiedene Tools zur Verfügung, wie z.B. sslscan oder testssl.sh. Letzteres haben wir bereits im vorangegangenen Lab installiert. Du kannst es folgendermaßen verwenden:
testssl localhost:443
Im Ergebnis steht der Overall Grade T, also fernab von A+. T steht für ein Vertrauensproblem (trust). Das ist aber nachvollziehbar, da wir in diesem Lab ein Self Signed Certificate nutzen. Unter dem Strich haben wir hier noch kein wasserdichtes TLS am Start, aber es ist an vielen Stellen schon gut gehärtet, wie ein Blick in die Details der Ausgabe des Befehls zeigt.