Eine API kann perfekt designed sein – wenn die Transportschicht unsicher ist, ist alles umsonst. Ein abgelaufenes Zertifikat, ein schwacher Cipher, ein Secret im Log: Jeder dieser Fehler kann zum Breach führen.
Dieser Artikel behandelt die oft übersehene Infrastruktur-Ebene: TLS richtig konfigurieren, Zertifikate automatisch rotieren, Secrets sicher verwalten und mTLS für Zero-Trust-Architekturen einsetzen.
Zielbild
Nach diesem Artikel kannst du:
- Eine sichere TLS-Policy definieren und durchsetzen
- Zertifikatsmanagement automatisieren
- mTLS für Service-to-Service-Kommunikation einrichten
- Secrets sicher speichern und rotieren
- At-Rest-Encryption für Datenbanken und Backups konfigurieren
Kernfragen
Bevor du weiterliest, versuche diese Fragen für dein Projekt zu beantworten:
- Welche TLS-Version ist Minimum? TLS 1.2? TLS 1.3?
- Wie rotiert ihr Zertifikate? Manuell? Let's Encrypt? Interne CA?
- Braucht ihr mTLS intern? Zero-Trust? Compliance?
- Wo liegen eure Secrets? Umgebungsvariablen? Vault? Cloud KMS?
- Ist At-Rest-Encryption aktiviert? Datenbank? Backups? Logs?
TLS: Die Grundlage
TLS (Transport Layer Security) verschlüsselt die Kommunikation zwischen Client und Server. Ohne TLS sind alle anderen Sicherheitsmaßnahmen wertlos.
TLS-Versionen
| Version | Status | Empfehlung |
|---|---|---|
| TLS 1.0 | Deprecated | Deaktivieren |
| TLS 1.1 | Deprecated | Deaktivieren |
| TLS 1.2 | Aktuell | Minimum |
| TLS 1.3 | Aktuell | Bevorzugt |
Warum TLS 1.3?
- Schnellerer Handshake (1-RTT statt 2-RTT)
- Keine unsicheren Cipher Suites mehr möglich
- Forward Secrecy ist Pflicht
Cipher Suites
Cipher Suites definieren, welche Algorithmen für Verschlüsselung verwendet werden.
TLS 1.3 Cipher Suites (empfohlen):
TLS_AES_256_GCM_SHA384
TLS_AES_128_GCM_SHA256
TLS_CHACHA20_POLY1305_SHA256
TLS 1.2 Cipher Suites (wenn 1.2 nötig):
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-RSA-AES128-GCM-SHA256
Verboten:
| Cipher | Problem |
|---|---|
| RC4 | Gebrochen |
| DES, 3DES | Zu schwach |
| MD5 | Gebrochen |
| SHA1 | Deprecated |
| CBC-Mode ohne AEAD | Padding Oracle |
| Export Ciphers | Absichtlich schwach |
| NULL Ciphers | Keine Verschlüsselung |
NGINX-Konfiguration
server {
listen 443 ssl http2;
server_name api.example.com;
# Zertifikate
ssl_certificate /etc/ssl/certs/api.example.com.crt;
ssl_certificate_key /etc/ssl/private/api.example.com.key;
# TLS-Versionen
ssl_protocols TLSv1.2 TLSv1.3;
# Cipher Suites (TLS 1.2)
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers on; # Nur relevant für TLS 1.2
# TLS 1.3 Cipher Suites (abhängig von NGINX/OpenSSL)
# ssl_conf_command Ciphersuites TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
# Session-Tickets (für Performance)
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off; # Oder mit Rotation
}
Security Headers
Für Browser-Clients sind zusätzliche Header nötig:
# HTTPS erzwingen
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Weitere Security Headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'none'; frame-ancestors 'none'" always;
HSTS (HTTP Strict Transport Security)
HSTS zwingt Browser, nur HTTPS zu verwenden:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
| Direktive | Bedeutung |
|---|---|
max-age |
Wie lange HSTS gilt (in Sekunden) |
includeSubDomains |
Gilt auch für Subdomains |
preload |
In Browser-Preload-Listen aufnehmen |
Wichtig: HSTS ist schwer rückgängig zu machen. Erst mit kurzem max-age
testen.
Zertifikatsmanagement
Zertifikate müssen regelmäßig erneuert werden. Manuell ist das fehleranfällig.
Let's Encrypt mit Auto-Renewal
Let's Encrypt bietet kostenlose Zertifikate mit 90-Tagen-Gültigkeit.
# Certbot installieren
apt install certbot python3-certbot-nginx
# Zertifikat erstellen
certbot --nginx -d api.example.com
# Auto-Renewal testen
certbot renew --dry-run
Certbot richtet automatisch einen Cron-Job ein:
# /etc/cron.d/certbot
0 */12 * * * root certbot renew --quiet
Interne CA für Microservices
Für interne Services ist eine eigene CA oft sinnvoller:
Tools:
- cfssl: Cloudflare's PKI toolkit
- step-ca: Smallstep's Certificate Authority
- Vault PKI: HashiCorp Vault's PKI Secrets Engine
Zertifikats-Rotation
| Zertifikatstyp | Gültigkeit | Rotation |
|---|---|---|
| Let's Encrypt | 90 Tage | Auto (60 Tage) |
| Interne Services | 30-90 Tage | Auto |
| Root CA | 10-20 Jahre | Manuell, geplant |
| Intermediate CA | 2-5 Jahre | Manuell, geplant |
Monitoring (Checkliste)
Zertifikate müssen überwacht werden:
# Prometheus Alert
groups:
- name: ssl
rules:
- alert: SSLCertificateExpiringSoon
expr: ssl_certificate_expiry_seconds < 7 * 24 * 60 * 60
for: 1h
labels:
severity: warning
annotations:
summary: "SSL certificate expires in less than 7 days"
mTLS (Mutual TLS)
Bei normalem TLS authentifiziert sich nur der Server. Bei mTLS authentifizieren sich beide Seiten mit Zertifikaten.
Wann mTLS?
| Szenario | mTLS? | Begründung |
|---|---|---|
| Public API | Nein | Clients haben keine Zertifikate |
| Interne Microservices | Ja | Zero-Trust, Service Identity |
| Partner-API | Optional | Zusätzliche Sicherheit |
| Hochsichere Umgebungen | Ja | Compliance, Defense in Depth |
mTLS-Handshake
NGINX mTLS-Konfiguration
server {
listen 443 ssl http2;
server_name internal-api.example.com;
# Server-Zertifikat
ssl_certificate /etc/ssl/certs/server.crt;
ssl_certificate_key /etc/ssl/private/server.key;
# Client-Zertifikat verlangen
ssl_client_certificate /etc/ssl/certs/ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;
# Client-Zertifikat-Info an Backend weitergeben
location / {
proxy_pass http://backend;
proxy_set_header X-Client-Cert-DN $ssl_client_s_dn;
proxy_set_header X-Client-Cert-Verify $ssl_client_verify;
}
}
Service Mesh (Istio, Linkerd)
Service Meshes automatisieren mTLS zwischen Services:
# Istio PeerAuthentication
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICT
Vorteile:
- Automatische Zertifikats-Rotation
- Keine Code-Änderungen nötig
- Zentrale Policy-Verwaltung
Secrets Management
Secrets (API Keys, DB-Passwörter, Encryption Keys) brauchen besonderen Schutz.
Wo Secrets NICHT hingehören
| Ort | Problem |
|---|---|
| Source Code | Im Repo für immer |
| Git History | Auch nach Löschen noch da |
| Logs | Oft unverschlüsselt gespeichert |
| Umgebungsvariablen | Potenziell auslesbar (z.B. via Debugging) |
| Config Files | Oft nicht verschlüsselt |
| Error Messages | An Clients gesendet |
Secrets Management Tools
| Tool | Use Case |
|---|---|
| HashiCorp Vault | Enterprise, Multi-Cloud |
| AWS Secrets Manager | AWS-native |
| GCP Secret Manager | GCP-native |
| Azure Key Vault | Azure-native |
| Kubernetes Secrets | Kubernetes-native (+ Sealed Secrets) |
| 1Password/Doppler | Team-Secrets, CI/CD |
Vault-Beispiel
# Secret speichern
vault kv put secret/api/database \
username="api_user" \
password="super-secret-password"
# Secret lesen
vault kv get secret/api/database
In der Anwendung:
import hvac
client = hvac.Client(url='https://vault.example.com')
client.token = os.environ['VAULT_TOKEN']
secret = client.secrets.kv.read_secret_version(path='api/database')
db_password = secret['data']['data']['password']
Secret Rotation
Secrets sollten regelmäßig rotiert werden:
| Secret-Typ | Rotation |
|---|---|
| DB-Passwörter | 90 Tage |
| API Keys | Bei Kompromittierung |
| Encryption Keys | 1 Jahr |
| JWT Signing Keys | 90 Tage |
| Service Account Tokens | 24 Stunden |
Rotation ohne Downtime:
- Neues Secret erstellen
- Beide Secrets akzeptieren (Grace Period)
- Anwendungen auf neues Secret umstellen
- Altes Secret deaktivieren
At-Rest Encryption
Daten müssen auch im Ruhezustand verschlüsselt sein.
Datenbank-Encryption
Managed Database mit Encryption (empfohlen):
# Terraform: AWS RDS
resource "aws_db_instance" "api" {
storage_encrypted = true
kms_key_id = aws_kms_key.database.arn
}
Alternative (Self-Hosted): Disk-/Volume-Encryption (z.B. LUKS/BitLocker)
oder spaltenweise Verschlüsselung via pgcrypto.
Backup-Encryption
# PostgreSQL Backup mit Encryption
pg_dump dbname | gpg --symmetric --cipher-algo AES256 > backup.sql.gpg
# Restore
gpg --decrypt backup.sql.gpg | psql dbname
Encryption-Hierarchie
Key Management
| Prinzip | Umsetzung |
|---|---|
| Separation of Duties | Wer Keys verwaltet ≠ Wer Daten verwaltet |
| Least Privilege | Keys nur für nötige Operationen |
| Audit | Jeder Key-Zugriff wird geloggt |
| Rotation | Keys regelmäßig rotieren |
| Backup | Keys sicher und getrennt backuppen |
TLS für Datenbank-Verbindungen
Auch interne Verbindungen sollten verschlüsselt sein.
PostgreSQL mit TLS
Server-Konfiguration (postgresql.conf):
ssl = on
ssl_cert_file = '/etc/ssl/certs/server.crt'
ssl_key_file = '/etc/ssl/private/server.key'
ssl_ca_file = '/etc/ssl/certs/ca.crt'
Client-Konfiguration (pg_hba.conf):
# Nur SSL-Verbindungen erlauben
hostssl all all 0.0.0.0/0 scram-sha-256
Connection String:
postgresql://user:pass@db.example.com:5432/dbname?sslmode=verify-full&sslrootcert=/path/to/ca.crt
SSL-Modi
| Modus | Beschreibung | Empfehlung |
|---|---|---|
| disable | Kein SSL | Nie |
| allow | SSL wenn Server will | Nein |
| prefer | SSL bevorzugt | Nein |
| require | SSL erforderlich | Minimum |
| verify-ca | + CA prüfen | Besser |
| verify-full | + Hostname prüfen | Empfohlen |
Regeln & Anti-Patterns
Do
- TLS 1.2 als Minimum, TLS 1.3 bevorzugt
- Nur sichere Cipher Suites (AEAD, Forward Secrecy)
- Automatische Zertifikats-Rotation (Let's Encrypt, Vault)
- Zertifikats-Expiry überwachen
- mTLS für interne Service-Kommunikation
- Secrets in Vault/Secrets Manager, nicht in Code
- Secret Rotation mit Grace Period
- At-Rest Encryption für Datenbanken und Backups
- TLS auch für interne DB-Verbindungen
Don't
- TLS 1.0/1.1 erlauben
- Schwache Ciphers (RC4, DES, MD5, SHA1)
- Selbstsignierte Zertifikate in Production (außer mit eigener CA)
- Zertifikate manuell rotieren
- Secrets in Git, Logs oder Umgebungsvariablen
- Secrets hardcoden
- Unverschlüsselte Backups
sslmode=disablefür Datenbanken
Artefakt: Security-Transport-Check
# Security-Transport-Check
## TLS-Konfiguration
### Protokolle
- [ ] TLS 1.0 deaktiviert
- [ ] TLS 1.1 deaktiviert
- [ ] TLS 1.2 aktiviert (Minimum)
- [ ] TLS 1.3 aktiviert (bevorzugt)
### Cipher Suites
- [ ] Nur AEAD-Ciphers (GCM, ChaCha20-Poly1305)
- [ ] Forward Secrecy (ECDHE)
- [ ] Keine RC4, DES, 3DES
- [ ] Keine MD5, SHA1
- [ ] Keine Export/NULL Ciphers
### Validierung
```bash
# TLS-Konfiguration testen
nmap --script ssl-enum-ciphers -p 443 api.example.com
# Oder mit testssl.sh
./testssl.sh api.example.com
```
## Zertifikate
### Public API
- [ ] Zertifikat von vertrauenswürdiger CA
- [ ] Auto-Renewal konfiguriert (Certbot/ACME)
- [ ] Expiry-Monitoring aktiv
- [ ] OCSP Stapling aktiviert
### Interne Services
- [ ] Interne CA eingerichtet
- [ ] Zertifikats-Rotation automatisiert
- [ ] mTLS zwischen Services aktiviert
### Monitoring
- [ ] Alert bei Expiry < 14 Tage (Warning)
- [ ] Alert bei Expiry < 7 Tage (Critical)
- [ ] Alert bei Zertifikatsfehler
## Security Headers (Checkliste)
- [ ] `Strict-Transport-Security` (HSTS)
- [ ] `X-Content-Type-Options: nosniff`
- [ ] `X-Frame-Options: DENY`
- [ ] `Referrer-Policy`
- [ ] `Content-Security-Policy` (wenn Browser-Clients)
## Secrets Management (Checkliste)
### Speicherung
- [ ] Secrets in Vault/Secrets Manager
- [ ] Keine Secrets in Git
- [ ] Keine Secrets in Logs
- [ ] Keine Secrets in Error Messages
### Rotation
| Secret | Rotation | Letzte Rotation |
|-----------------|------------|-----------------|
| DB-Passwort | 90 Tage | YYYY-MM-DD |
| JWT Signing Key | 90 Tage | YYYY-MM-DD |
| API Keys | Bei Bedarf | YYYY-MM-DD |
| Encryption Keys | 1 Jahr | YYYY-MM-DD |
### Zugriff
- [ ] Least Privilege für Secret-Zugriff
- [ ] Audit-Log für Secret-Zugriffe
- [ ] Separate Secrets pro Umgebung
## At-Rest Encryption (Checkliste)
### Datenbank
- [ ] Encryption aktiviert
- [ ] KMS-managed Keys
- [ ] Key Rotation konfiguriert
### Backups
- [ ] Backups verschlüsselt
- [ ] Backup-Keys getrennt von Daten-Keys
- [ ] Backup-Restore getestet
### Logs
- [ ] Sensitive Daten nicht in Logs
- [ ] Logs verschlüsselt (wenn persistent)
## Datenbank-Verbindungen
- [ ] TLS aktiviert (`sslmode=verify-full`)
- [ ] Server-Zertifikat von interner CA
- [ ] Client-Zertifikat (wenn mTLS)
## Test-Befehle
```bash
# TLS-Version und Ciphers prüfen
openssl s_client -connect api.example.com:443 -tls1_3
# Zertifikat anzeigen
openssl s_client -connect api.example.com:443 </dev/null 2>/dev/null | openssl x509 -text
# Expiry-Datum
echo | openssl s_client -connect api.example.com:443 2>/dev/null | openssl x509 -noout -enddate
# Headers prüfen
curl -I https://api.example.com
# SSL Labs Test
# https://www.ssllabs.com/ssltest/
```
Checkliste
Bevor du zum nächsten Artikel gehst, prüfe:
- [ ] TLS 1.2+ ist Minimum, TLS 1.3 bevorzugt
- [ ] Nur sichere Cipher Suites sind aktiviert
- [ ] HSTS ist konfiguriert (für Browser-Clients)
- [ ] Zertifikate rotieren automatisch
- [ ] Zertifikats-Expiry wird überwacht
- [ ] mTLS ist für interne Services aktiv (oder geplant)
- [ ] Secrets liegen in Vault/Secrets Manager
- [ ] Secret Rotation ist definiert
- [ ] At-Rest Encryption ist aktiv (DB, Backups)
- [ ] DB-Verbindungen nutzen TLS
- [ ] Security-Transport-Check ist dokumentiert
Wie es weitergeht
Im nächsten Teil geht es um OWASP API Security: Die Top-10-Risiken für APIs und wie du sie in deinem Design von Anfang an verhinderst.
Alle Teile der Serie: Serie: API-Design