Nginx absichern

Nginx absichern

Viele Webserver und deren Webapplikation laufen ohne tiefgreifendere Konfigurationen. Dabei verpasst man die Chance sich und seine Besucher besser vor Angriffen zu schützen. Nur wenige Einstellungen erwarten eine wirklich tief gehende Kenntnis über deren Funktion und ermöglicht somit allen Admins recht einfach eine ordentlich robusten Webserver zu betreiben.

In der Grundeinstellung posaunt der Nginx stets seine Versionsnummer raus. Das ist für Angreifer die einfachste Einladung und muss abgestellt werden. Mit der Option “server_tokens”, kann die Ausgabe gesteuert werden. Stellt daher den Servertoken auf “off”, damit wird der Header des Dienstes auf die Ausgabe “Nginx” reduziert. Die Version des Nginx kann man dennoch über Tools ermitteln, es erschwert nur die Erkennung und reduziert “DriveBy”-Angriffe.

server_tokens off

TLS/SSL

Standardinstallation haben meist kein TLS aktiviert. Wenn es aktiviert ist hat das Protokoll meist eine viel zu hohe Toleranz bei der Verschlüsselung und führt das ganze wiederum ad absurdum. Hier ist eine robuste Grundkonfiguration, die man später auch in einzelnen Server-Einträgen überschreiben kann.

Zunächst setzen wir grundlegende Werte für die Nutzung der TLS-Sessions

ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_buffer_size 8k;

Nun setzen wir die zu unterstützenden Protokolle und Ciphers (Gibt ein A+ Rating)

ssl_prefer_server_ciphers on;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ‘TLS13-AES-256-GCM-SHA384:AES256+EECDH:AES256+EDH:!aNULL:!ECDHE-RSA-AES256-SHA384:!ECDHE-RSA-AES256-SHA:!DHE-RSA-AES256-SHA256:!DHE-RSA-AES256-SHA’;
ssl_ecdh_curve secp384r1;

Diese Einträge müssen in die jeweilge Server/Domain-Konfiguration. Die Datei .pem muss das Domain-Zertifikat und ein evtl. nötiges Zwischenzertifikat enthalten. Der Private-Key des Domain-Zertifkates wird mit .key hinterlegt

ssl_certificate /etc/nginx/certificates/tls.pem;
ssl_certificate_key /etc/nginx/certificates/tls.key;

DHE

Wir können für die DHE-Ciphers einen eigenen DHE Parametern mit eigener Stärke hinterlegen. Wir müssen dazu die folgende Konfiguration hinterlegen. Diese kann global oder besser noch, für jede Domain individuell hinterlegt werden.

ssl_dhparam /etc/nginx/certificates/dhparam.pem;

Mit dem folgenden Befehl, kann man die Datei erzeugen.

openssl dhparam -out /etc/nginx/certificates/dhparam.pem 4096

Wer möchte, kann seine Konfiguration unter https://ssllabs.com/ssldb/ testen.

Wenn du mehr über Zertifikate wissen möchtest, ich habe in meinem Blog das Thema X509 Zertifikate angeschnitten

HSTS

Mit HTTP Strict Transport Security kann man festlegen, ob der Client/Browser die (Sub-)Domain nur via TLS ereichen soll. Nach der ersten Verbindung mit dem Server, wird im Client (z.B. Chrome und FireFox) die HSTS-Option gesetzt und selbst wenn jetzt eine HTTP-URL der (Sub-)Domain geöffnet wird, wird automatisch auf der Clientseite schon auf HTTPS umgestellt. Diese Option reduziert die Möglichkeit, dass man per DNS Manipulationen dem Client eine gefälschte Seite unter jubeln kann. Diese Option benötigt das Header-Modul.

add_header Strict-Transport-Security “max-age=31536000;includeSubDomains;preload”;

  • max-age
    Die Gültigkeit der Regel in Sekunden
  • includeSubDomains
    Die Regel gilt ebenfalls für alle Subdomains dieser Domain
  • preload
    Mit der Option kann man sich bei dieser Seite registrieren: https://hstspreload.org und wir damit “hardcoded” als HTTPS-Domain in dem Chrome Browser hinterlegt - Damit ist die Domain, für diese Browser, nur noch via HTTPS erreichbar.

Damit gesetzte Cookies nicht von Scripten genutzt werden können, kann man im Nginx einen entsprechenden Header setzen. Man reduziert damit erheblich die Möglichkeit eines XSS Angriffs.

set_cookie_flag HttpOnly secure;

OCSP

Online Certificate Status Protocol prüft, ob das vom Server ausgelieferte Zertifikat noch gültig ist. Ist dem nicht so, wird eine entsprechende Meldung an den Client gesendet. Die Prüfung des Zertifikates erfolgt, vom Webserver selbst, direkt beim CA-Aussteller via HTTP. Evtl. benötigt eure Firewall ein weiteres RuleSet für das OCSP-Protokoll.

ssl_stapling_verify on;
ssl_stapling on;
ssl_trusted_certificate “/etc/pki/tls/certs/ca-bundle.crt”;

X-Frame-Options

Mit dieser Option lässt es sich unterbinden, dass die eigene Seite und teile davon in Frames dargestellt werden können. Damit verhindert man z.B. die Darstellung der eigenen Seite unter einer anderen Domain. Diese Sicherheitsoption ist in fast allen neueren Browser aktiviert, kann aber gezielt umgangen werden. Es ist eine Sicherheitsfunktion für den Browser-Nutzer.

add_header X-Frame-Options “SAMEORIGIN”;

Es gibt folgende Optionen

  • DENY
    Es ist verboten die Seite in einem Frame darzustellen.
  • SAMEORIGIN
    Nur der eigene Domain ist dies erlaubt.
  • ALLOW-FROM https://example.com
    Mit Angabe welche Domain dies darf.

X-Content-Type-Options

Diese Option teilt dem Browser mit, dass er z.B. CSS-Dateien nur als solchen nutzen soll, wenn die Datei auch den entsprechenden MIME-Typ “text/css” entspricht - die Logik gilt auch für Scripte.

add_header X-Content-Type-Options “nosniff”;

X-XSS-Protection

Mit der Option kann man ein XSS Schutz aktivieren und bei Bedarf sogar das Laden der Seite blocken

add_header X-XSS-Protection “1; mode=block”;

  • 0
    Deaktiviert den Schutz
  • 1
    Aktiviert den Schutz
  • 1; mode=block
    Wird ein Cross-Site-Script entdeckt, wird das laden der Seite geblockt.

Content-Security-Policy (CSP)

Mit CSP wird festgelegt, woher welche Ressourcen genutzt werden dürfen. Die Option ist recht Komplex und sollte sehr bedacht eingesetzt werden. Wenn ihr externe Ressourcen nutzt und diese auf bestimmte URIs oder URLs beschränken möchtet, kann man das mit CSP recht genau definieren.

Hier ist eine Grundgerüst welches nur lokale Ressourcen erlaubt und noch nicht blocked. In der Browser-Konsole werden die Verstöße durch den Browser gemeldet. Wenn in CSP-Regel eine “report-uri” hinterlegt wird, wird zusätzlich ein JSON-Report dorthin geschickt.

add_header content-security-policy-report-only “upgrade-insecure-requests;block-all-mixed-content;default-src ‘self’;base-uri ‘self’;form-action ‘self’;frame-ancestors ‘self’;”

Optionen

  • default-src
    Gilt für alle Regeln, wenn diese nicht nachstehend explizit überschrieben werden.
    !Gilt nur für Regeln die mit “-src” enden!
  • connect-src
    Definiert zu welcher Adresse XHR, WebSockets und EventSource genutzt werden dürfen Definiert aus welcher Quelle Inhalte in Frames dargestellt werden dürfen
  • font-src
    Definiert aus welcher Quelle Schriften geladen werden dürfen
  • form-action
  • frame-src
    Definiert zu welcher Adresse Formulare ausgelöst werden dürfen
  • img-src
    Definiert aus welcher Quelle Bilder geladen werden dürfen
  • object-src
    Definiert aus welcher Quelle Plugins ausgeführt werden dürfen
  • report-uri
    Spezifiert eine URL, wohin der Browser Regelverstöße senden soll.
  • script-src
    Definiert aus welcher Quelle Skripts ausgeführt werden dürfen
  • style-src
    Definiert aus welcher Quelle CSS geladen werden dürfen

Werte

  • ’none’
    Der Einsatz ist nicht erlaubt
  • ‘self’
    Nur der Quelle ist es erlaubt
  • ‘unsafe-inline’
    Erlaubt inline JavaScript und CSS
  • ‘unsafe-eval’
    Erlaubt Text-to-JavaScript Techniken wie eval
  • ‘data:’
  • ‘blob:’

Als Werte können ebenfalls URIs, URLs oder Protokolle angegeben werden

Einen guten Einblick gibt die W3C Spezifikation unter http://www.w3.org/TR/CSP/. Der Einsatz ist nicht immer ganz unproblematisch, da die typischen Browser (IE/IOS/Opera) wie immer der Technik nicht folgen können/wollen. Einen guten Überblick zeigt die folgende Seite http://caniuse.com/#feat=contentsecuritypolicy

Subresource Integrity (SRI)

Diese Einstellung betrifft zwar nicht den Nginx selbst, da ich aber überhaupt kein Fan von extern eingebunden Ressourcen bin, erwähne ich hier die Möglichkeit das ganze etwas abzumildern. Neben der Sicherheit leidet meistens auch noch der Datenschutz, daher unterlasst das einbinden von externen Ressourcen, Bitte! Wer es unbedingt tun muss, sollte zu mindestens die externen Ressourcen hashen und diesen beim include in der Webseite mit angeben. Das ist das absolute Minimum, um euren Besuchern das minimale Gefühl zu geben, das euch eure Besucher irgendetwas etwas Wert sind. Die folgenden Schritte können für Javascript und auch für CSS Einbindungen genutzt werden.

<script src=“https://code.jquery.com/jquery-3.1.0.min.js" integrity=“sha384-R4/ztc4ZlRqWjqIuvf6RX5yb/v90qNGx6fS48N0tRxiGkqveZETq72KgDVJCp2T” crossorigin=“anonymous”></script>

Um einen Hash für eigene Includes zu generieren, müsst ihr wie folgt einen Aufruf absetzen. Dieser erzeugt einen Hash über die externe Ressource und verbastelt den in eine Base64 Code. Den Hash tragt ihr dann in das Attribute “integrity” ein. Die Angabe “crossorigin=‘anonymous’” verhindert die Übertragung eines Cookies an den externen Server.

curl -s https://code.jquery.com/jquery-3.1.0.min.js | \
openssl dgst -sha384 -binary | \
openssl base64 -A

Ausgabe

R4/ztc4ZlRqWjqIuvf6RX5yb/v90qNGx6fS48N0tRxiGkqveZETq72KgDVJCp2T

HPKP - Host Public Key Pinning

Den Abschnitt habe ich entfernt, da die Technik von den neueren Browsern nicht mehr unterstützt wird.

Referrer

Um die Privatssphäre besser zu schützen, kann man den Browser dazu auffordern, beim Aufruf einer URL bzw. Links, keinen Verweis mitzusenden woher der Aufruf kam. Das ist in er Regel eine interessante Information für Webseitenbetreiber oder eine nützliche Information für Javascripte, allerdings sollte die Privatssphäre höcher gewichtet werden. Die Option kann auch auf die eigene Domain eingeschränkt werden.

add_header Referrer-Policy “no-referrer”;

Hier sind einige Referrer-Optionen:

  • no-referrer
    Es wird beim einer Anfrage kein Referrer übergeben
  • no-referrer-when-downgrade
    Wenn die Verbindung auf HTTP gedowngraded wird, wird kein Referrer übergeben.
  • origin
    Übergibt nur den Domainnamen, nicht die gesamt URL. Dies gilt für verschlüsselte sowie unverschlüsselte Verbindungen
  • origin-when-cross-origin
    Innerhalb der Domain wird die gesamte URL übergeben, ansonsten nur der Domainname
  • same-origin
    Es wird nur innerhalb der Domain ein Referrer übergeben
  • strict-origin
    Übergibt den Referrer nur bei verschlüsselten Verbindungen
  • strict-origin-when-cross-origin
    Innerhalb der Domain wird die gesamte URL übergeben, ansonsten nur der Domainname. Das gilt allerdings nur, wenn es sich um eine verschlüsselte Verbindung handelt.
  • unsafe-url Übergibt auch bei nicht verschlüsselten Verbindungen innerhalb der eigenen Domain einen Referrer