sslxy

server-haertung

TLS, Header, CSP, Trusted Types, Rate Limiting – A+ als Ergebnis sauberer Arbeit, nicht als Selbstzweck.

Server-Härtung beginnt nicht mit einem Tool-Score und endet auch nicht dort. Sie beginnt mit dem Verständnis, welche Angriffsfläche eine Website überhaupt hat, und sie lebt davon, dass jede gesetzte Direktive einen bekannten Zweck erfüllt.

Diese Seite beschreibt keinen Sicherheitsaberglauben und kein Copy-Paste aus Generatoren. Sie beschreibt die nüchterne Praxis: HTTPS ohne Altlasten, sinnvolle Security-Header, eine passende CSP, klare Grenzen für Browser-Funktionen und ein Betrieb, der Logs tatsächlich liest.

Security Snapshot

> AUDIT OVERVIEW
TLS nur moderne Protokolle, keine nostalgischen Zugeständnisse HEADERS HSTS, CSP, nosniff, frame-ancestors, Referrer-Policy, Permissions-Policy BROWSER weniger implizites Vertrauen, mehr explizite Grenzen BETRIEB Rate Limiting, Log-Sicht, kurze Reaktionswege HALTUNG verstehen statt abschreiben
Sicherheit ist keine Plakette. Sie ist eine fortlaufende Pflegearbeit.
[mindset/first]

Grundhaltung: verstehen statt kopieren

Die häufigste Fehlentwicklung bei Server-Härtung ist nicht zu wenig Technik, sondern zu wenig Verständnis. Man übernimmt Konfigurationsblöcke aus Foren, Generatoren oder Blogposts, bekommt vielleicht einen grünen Testbericht und merkt erst später, dass die Hälfte davon nicht zur eigenen Seite passt.

Eine saubere Konfiguration beantwortet drei Fragen: Was schützt diese Direktive? Welchen Nebeneffekt hat sie? Wie prüfe ich nach einer Änderung, ob sie noch korrekt greift? Wenn eine dieser drei Antworten fehlt, ist die Zeile in der Konfiguration bereits verdächtig.

[regeln] > keine Direktive ohne begründeten Zweck > keine Policy ohne Browser-Test und Fehlersicht > kein Vertrauen in A+, wenn die Logik dahinter unklar bleibt > keine externe Abhängigkeit ohne bewusste Erlaubnis

„Ein guter Security-Score ist kein Ziel. Er ist nur ein Nebenprodukt sauberer Entscheidungen.“

[transport/tls]

TLS: der Transport muss zuerst stimmen

TLS ist die Grundlage. Ohne saubere Transportverschlüsselung sind alle späteren Header nur Kosmetik. Der erste Schritt ist deshalb schlicht: HTTP konsequent nach HTTPS umleiten und auf veraltete Protokollgenerationen verzichten.

  • HTTP → HTTPS: ausnahmslos umleiten, nicht optional anbieten.
  • Nur moderne Protokolle: keine historischen Versionen mehr mitschleppen, nur weil einzelne Altgeräte sonst noch durchkommen.
  • Zertifikate: vollständige Chain, automatische Erneuerung und Prüfung nach Änderungen.
  • OCSP Stapling: sinnvoll, wenn der Server es sauber ausliefert und der Resolver korrekt steht.
server { listen 80; listen [::]:80; server_name example.com www.example.com; return 301 https://$host$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name example.com www.example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on; }
[hsts/preload]

Wenn HSTS mit preload verwendet wird, müssen includeSubDomains und ein max-age von mindestens einem Jahr vorhanden sein. Das setzt eine wirklich vollständige HTTPS-Disziplin für die gesamte Domainfamilie voraus.

[policy/csp]

CSP: die eigentliche Grenze gegen unnötiges Vertrauen

Eine Content-Security-Policy ist nur dann gut, wenn sie zur Seite passt. Für eine rein statische Seite ohne fremde Skripte ist die CSP erfreulich streng. Für komplexere Seiten muss sie bewusst erweitert werden. Was ich nicht tue: eine universelle „irgendwie grüne“ Policy aus dem Netz übernehmen.

Für klassische Informationsseiten ist dieser Aufbau ein sauberer Ausgangspunkt:

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; media-src 'self'; object-src 'none'; frame-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests;

Der Unterschied zwischen einer guten und einer schlechten CSP liegt oft in zwei Dingen: erstens dem Verzicht auf bequeme Weichmacher wie 'unsafe-inline', zweitens in ehrlicher Prüfung mit Report-Only, bevor blockiert wird.

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-uri /csp-report;

Für stark statische Seiten kann statt Nonce auch ein Hash für kleine Inline-Skripte sinnvoll sein. Für dynamische Anwendungen ist Nonce oft praktischer. Beides ist sauberer als globale Inline-Freigaben.

[dom/trusted-types]

Trusted Types: nur dort relevant, wo DOM-Sinks wirklich benutzt werden

Trusted Types ist kein allgemeiner Schmuck für jede Website. Es ist dort interessant, wo JavaScript mit riskanten DOM-Sinks wie innerHTML arbeitet und wo man den Fluss zu diesen Sinks bewusst kontrollieren will. Für eine rein statische Seite ohne solche Muster ist es nicht der erste Hebel.

Wenn es eingesetzt wird, dann nicht als bloßer String-Durchreicher, sondern über eine klar definierte Policy, die HTML bewusst aufbereitet oder sanitisiert.

Content-Security-Policy: require-trusted-types-for 'script'; trusted-types ochsen-policy;
if (window.trustedTypes && trustedTypes.createPolicy) { const policy = trustedTypes.createPolicy("ochsen-policy", { createHTML: (input) => input.replace(/</g, "&lt;") }); const safeHtml = policy.createHTML(userInput); target.innerHTML = safeHtml; }
[trusted-types]

Trusted Types ersetzt keine saubere CSP und auch keine inhaltliche Sanitization. Es ist eine zusätzliche Sperrschicht gegen DOM-basiertes XSS, nicht die ganze Lösung.

[operations/rate-limiting]

Rate Limiting: kleine Bremse, großer Unterschied

Nicht jede Seite braucht dieselbe Härte. Aber fast jede öffentlich erreichbare Seite profitiert davon, wenn einzelne Clients nicht unbegrenzt und ohne Taktung feuern können. Für Login- oder Formular-Endpunkte ist das Pflicht. Für statische Seiten ist es zumindest ein vernünftiger Filter gegen den alltäglichen Lärm.

limit_req_zone $binary_remote_addr zone=allgemein:10m rate=30r/m; limit_conn_zone $binary_remote_addr zone=conn_limit:10m; server { limit_req zone=allgemein burst=10 nodelay; limit_conn conn_limit 20; }

Rate Limiting ersetzt keine Zugangskontrolle und auch kein Fail2Ban, aber es nimmt Druck aus vielen banalen Angriffsmustern und macht Logbilder lesbarer.

[operations/logs]

Logs: nur hilfreich, wenn sie wirklich gelesen werden

Security ohne Log-Sicht ist blind. Ein Scanner, der nachts zehnmal nach /.env, /wp-login.php oder alten PHPMyAdmin-Pfaden fragt, ist kein Weltuntergang. Aber man sollte wissen, dass er da war, wie häufig er auftaucht und ob das Muster kippt.

  • 404-Cluster: zeigen typischen Scanverkehr und geben ein Gefühl für den Hintergrundlärm.
  • 429-Antworten: verraten, ob Rate Limiting greift oder zu locker ist.
  • TLS-Fehler: zeigen, ob veraltete Clients, Bots oder Konfigurationsfehler aufschlagen.
  • Ungewöhnliche User-Agents: sind nicht automatisch böse, aber oft ein guter Startpunkt für Blicktiefe.
log_format security '$remote_addr - [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' '$request_time'; access_log /var/log/nginx/access.log security; error_log /var/log/nginx/error.log warn;

Gleichzeitig gilt: Access-Logs enthalten personenbezogene Daten. Kurze Aufbewahrung, klarer Zweck und kein Sammeltrieb.

[config/apache-nginx]

Apache und nginx: saubere Grundbausteine

Ich halte Security-Header und TLS-Parameter gern in wiederverwendbaren Snippets. Nicht, weil Modularisierung schön aussieht, sondern weil Pflegefehler sonst schnell auf mehreren Hosts gleichzeitig auseinanderlaufen.

nginx

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; add_header X-Frame-Options "DENY" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), fullscreen=(self), web-share=()" always; add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; media-src 'self'; object-src 'none'; frame-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests;" always; server_tokens off;

Apache

<IfModule mod_headers.c> Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" "expr=%{HTTPS} == 'on'" Header always set X-Frame-Options "DENY" Header always set X-Content-Type-Options "nosniff" Header always set Referrer-Policy "strict-origin-when-cross-origin" Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), fullscreen=(self), web-share=()" Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; media-src 'self'; object-src 'none'; frame-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests;" </IfModule> ServerTokens Prod ServerSignature Off Protocols h2 http/1.1
[apache/http2]

Wer Apache mit HTTP/2 sauber fahren will, setzt das nicht nur „gefühlt“, sondern ausdrücklich über Protocols h2 http/1.1 und hält den Rest der TLS-Konfiguration schlank und nachvollziehbar.

[operations/checkliste]

Checkliste vor Go-Live oder größerer Änderung

Transport

HTTPS aktiv
HTTP sauber umgeleitet
Zertifikatskette vollständig
Erneuerung getestet
OCSP Stapling geprüft

Header

HSTS bewusst gesetzt
nosniff vorhanden
Referrer-Policy gesetzt
Permissions-Policy sinnvoll beschränkt
X-Frame-Options oder frame-ancestors geprüft

CSP

keine unnötigen Wildcards
kein gedankenloses unsafe-inline
Report-Only vor scharfem Block
Browser-Konsole ohne CSP-Rauschen

Betrieb

Rate Limiting aktiv
Logs lesbar und rotierend
Backups vorhanden
Updates planbar
Test mit echten Browsern und echten Requests

„Eine sichere Konfiguration ist nicht die längste. Sie ist die, die auch nach Monaten noch verstanden und gepflegt wird.“