Mehr Website-Sicherheit durch HTTP-Header-Optimierung #1 – HSTS & X-XSS-Protection

mehr-website-sicherheit-mit-http-header-hsts-xss-protection-banner

Zum Start dieser kleinen Serie, inspiriert durch einen Sicherheitsreport meines Blogs von enginsight.com (dazu mehr im November), erst einmal die Grundlagen der HTTP-Header:

Was sind HTTP-Header und wofür brauche ich sie?

HTTP-Header, oder in diesem Artikel vor allem die HTTP-Header-Respond-Felder, sind ein Teil der Antwort eines Webservers an den anfragenden Client (mehr dazu). Sie enthalten Anweisungen, Informationen und Einschränkungen der Verbindung zwischen Browser und Server, aufgebaut als eine Vielzahl an Schlüssel-Wert-Paaren.
Der Betreiber einer Webseite oder eines Webservers kann diese Antwortfelder teilweise konfigurieren und damit die Sicherheit der Webseite und des Verbindungsaufbaus erhöhen. Die Felder, die speziell für mehr Sicherheit entwickelt wurden, werden auch kurz HTTP-Security-Header genannt.

Die Anpassung der Header kann unterschiedlich erfolgen und ist vom Webserver oder der Webhosting Umgebung abhängig. Webserver bieten für HTTP gewöhnlich Konfigurationsdateien an, teilweise gibt es diesen Zugriff auch in root-Webhosting-Lösungen. Für „normales“ Webhosting geht das größtenteils auch über eine .htaccess in der obersten Ebene. Das könnte im Falle meines Webhosters All-Inklusive beispielsweise sein:
/www/htdocs/w00bxxxx/.htaccess

Mögliche Probleme mit .htaccess bei Shared Webhosting

Achtung: Eventuell werden Header Anpassungen von eurem Hoster geblockt/entfernt. Das liegt dann an der Art und Weise der Webserver-Konfiguration.
Sidestory: Bei meinem Hoster All-Inklusive, dessen Shared Hosting Server größtenteils PHP über FPM/FastCGI realisieren, ist das beispielsweise der Fall. Hier müssten die Einstellungen direkt im VHost gesetzt werden, das kann nur der Hoster selbst. Freundlicherweise hat mich All-Inkl nach einer Nachfrage auf eine moderne Serverfarm umgezogen, wo ich mittels .htaccess auch Header setzen kann.
Sollte das für euch nicht möglich sein, funktioniert für WordPress auch der Workaround über die functions.php, den ihr auf dieser Seite ganz unten im letzten Absatz findet. 
Teilweise kann es auch sein, dass die Einträge durch Einstellungen im Admin-Backend überschrieben werden. So kann es sein, dass HSTS, „Enforce SSL“ und ähnliche Domain-basierte Eigenschaften in der Domänenverwaltung angepasst werden und die .htaccess Angaben überschreiben.

Hier die Analyse meines Blogs von securityheaders.com vor der Optimierung der Security Header:

mehr-website-sicherheit-mit-http-header-hsts-xss-protection-header-analyse-davor
Security Header Analyse vorher mit der Endwertung D, nicht so vorteilhaft…

Kein sehr gutes Bild. Wird Zeit, daran zu arbeiten! Wir beginnen mit zwei recht unkomplizierten Security Feldern, HSTS und XSS-Protection. Sie bieten einen guten Einstieg in das Themengebiet und benötigen jeweils nur 1 Zeile in der .htaccess. Aber nun in die Details:

HTTP Strict Transport Security – SSL & HTTPS, bis dass der Tod uns scheidet

HSTS (HTTP Strict Transport Security) sollte ausschließlich aber unbedingt dann konfiguriert werden, wenn die Webseite SSL verschlüsselt (über https) erreichbar ist und nicht mehr über unverschlüsseltes HTTP erreichbar sein soll. HSTS gibt vor, dass und für welchen Zeitraum in der Zukunft alle Anfragen über SSL vom Client zum Server geschickt werden müssen.

<IfModule mod_headers.c>
  # Die einfache HSTS Variante, halbes Jahr Zeitfenster
  # Header always set Strict-Transport-Security "max-age=15778463"
  # oder noch sicherer mit Subdomains, preload und allen Subdomains
  Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
</IfModule>

Zwei Werte sind hier wichtig: max-age und der optionale Parameter includeSubDomains. max-age ist die Zeitangabe in Sekunden, wie lange der Zugriff in Zukunft für diesen gerade aufrufenden Client ausschließlich über SSL erfolgen muss. Der Wert 31536000 steht demnach für 1 Jahr. Ist die Webseite innerhalb dieser Zeit nicht über https erreichbar, weil beispielsweise das Zertifikat abgelaufen ist, kann sie nicht mehr geöffnet werden. Browser geben dann diese Art von Fehlermeldung und für den Nutzer ist hier Endstation. Das kann jetzt für den Nutzer doof sein, sorgt aber für ein hohes Sicherheitsminimum, das nicht unterschritten werden darf. Eigentlich ein Plus.

Ein Blick in die weiteren Eigenschaften von HSTS lohnt sich

Die zusätzliche Angabe includeSubDomains erweitert dieses Verfahren auf alle Subdomains, ein Wildcard-Zertifikat ist hier also von Nöten. 
Zu guter Letzt gibt es noch die preload-Eigenschaft, die noch nicht Teil des offiziellen Standards ist. Es ist die Crème de la Crème der HSTS-Absicherung und implementiert den HSTS-Schutz für eure Domain explizit und direkt in alle modernen Browser (akt: Chr, FF, Opr, Saf, IE11, Edg). Dafür muss die Seite jedoch HSTS ordentlich eingebunden haben und auf dieser Seite registriert werden. Dieser Artikel beschreibt sehr ausführlich und einfach dieses Attribut und all seine Details.
Prüft die HSTS-Eigenschaften eurer Seite beispielsweise hier. Mehr Informationen zu HSTS auch hier. Einen ausführlichen Check eurer SSL-Servereigenschaften könnt ihr hier initiieren – das dauert 2, 3 Minuten, ist aber sehr ausführlich und informativ.

X-XSS-Protection – steckt mehr drin als gedacht

Als nächste Eigenschaft schauen wir auf das Feld X-XSS-Protection. Dieses steuert Browser-Features, welche XSS-Attacken erkennen und verhindern. Diese Mechanismen sind normalerweise aktiviert, können aber von Servern oder vom Nutzer deaktiviert werden; X-XSS-Protection erzwingt die Nutzung dann entsprechend der Angabe. Oft lese ich, dass der Header nur von IE und Chrome, teilweise auch von Safari ausgewertet wird. Laut MDN wird der Header jedoch von allen Browsern bis auf Firefox unterstützt. Folgende Umsetzung via .htaccess:

<IfModule mod_headers.c> 
  # Schutz aktivieren und Rendern verhindern bei einem Angriff
  # Header always set X-XSS-Protection "1; mode=block" 
  # oder noch besser: Seite aufräumen, rendern und Angriff melden:
  Header always set X-XSS-Protection "1; report=http://reportingserver.com/reporting_URI.php
</IfModule>

Relativ einfach: Der erste Wert 1 erzwingt-aktiviert den Schutz, mode=block stoppt das Rendering der Seite und blockiert sie komplett. Ohne mode=block wird die Anfrage gereinigt und dann ausgeführt. Soweit so einfach.

XSS-Protection Reporting – Mitlesen der Angriffe

Nun kommt jedoch noch eine spannendere Anweisung, die leider auf kaum einer Infoseite beschrieben wird; vermutlich, weil es etwas mehr Text brauch. Denn die report-Anweisung ermöglicht die Angabe einer Reporting-URI, an die ein Bericht des Angriffs geschickt wird. Das dortige Skript kann nun beispielsweise loggen, benachrichtigen oder beliebig anders reagieren. Das finde ich besonders wert- und sinnvoll, daher hier noch der Aufbau:
Beginnen wir mit dem beispielhaften Aufbau eines XSS-Protection Report Requests, welcher mittels POST an die URI geschickt wird:

POST http://test.local/foo HTTP/1.1
Host: test.local
Connection: keep-alive
Content-Length: 116
Pragma: no-cache
Cache-Control: no-cache
Origin: http://test.local
X-FirePHP-Version: 0.0.6
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.65 Safari/537.36
Content-Type: application/json
Accept: */*
DNT: 1
Referer: http://test.local/test.php?foo=%3Cscript%3Ealert(1);%3C/script%3E
Accept-Encoding: gzip, deflate
Accept-Language: cs,en-US;q=0.8,en;q=0.6

{"xss-report":{"request-url":"http://test.local/test.php?foo=%3Cscript%3Ealert(1);%3C/script%3E","request-body":""}}

Nun wird der Request gegen ein PHP-Skript geschickt und dort empfangen, in eine Datei geloggt und per Mail an den Admin geschickt. Dazu habe ich diesen MDN-Code etwas erweitert: Es gibt jetzt mehr Mailing-Optionen und die Möglichkeit, eine E-Mail bei jedem Angriff zu senden, statt nur beim ersten:

<?php
// Start configure
$log_file = dirname(__FILE__) . '/xss-report.log';
$log_file_size_limit = 10000000; // in bytes = 10MB - once exceeded no further entries are added
$email_every_attack = true;
$email_sender = 'phpmailer@mysite.de';
$email_recipient = 'admin@mysite.de';
$email_subject = 'XSS Violation';
$email_charset = 'utf-8';
// End configuration

$current_domain = preg_replace('/www\./i', '', $_SERVER['SERVER_NAME']);
$email_subject = $email_subject . ' on ' . $current_domain;
http_response_code(204); // HTTP 204 No Content
$json_data = file_get_contents('php://input');

// We pretty print the JSON before adding it to the log file
if ($json_data = json_decode($json_data)) {
  $json_data = json_encode($json_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
  if ($email_every_attack) {
    // Send an email
    $message = "XSS violation on " . $current_domain . ":\n\n" .
      $json_data . "\n\n" .
      "Logfile:" . $log_file;
    $headers =  "From: ".$email_sender."\r\n".
                "Content-Type: text/plain;charset=".$email_charset;
    mail($email_recipient, $email_subject, $message, $headers);
  } else if (filesize($log_file) > $log_file_size_limit) {
    exit(0);
  }
  file_put_contents($log_file, $json_data."\n", FILE_APPEND | LOCK_EX);
}
?>

Am besten testet ihr die Reporting-Funktion vorher, beispielsweise über einen online API-Tester. Baut euch einfach den Request zusammen und schickt ihn an eure Report-URI. Das funktioniert gut und dient mir nun als Informationsquelle, welche XSS-Angriffsversuche mit exakt welchen Anfragen gegen meine Seite gefahren werden. Hier nochmal das Setup visuell:

mehr-website-sicherheit-mit-http-header-hsts-xss-protection-report-setup_thumb
XSS-Protection Reporting Setup mit Logging und Mailing

Es sei erwähnt, dass die X-XSS-Protection durch die neuere und komplexere Content Security Policy (CSP) ersetzt wird. Das schauen wir uns im nächsten Artikel genauer an. Solange dieses Feld aber noch nicht komplett von allen Browsern unterstützt wird, empfiehlt es sich weiterhin, XSS-Protection zu verbauen (zumal es so einfach ist).

Abschluss der ersten Optimierung

Die ersten zwei Security Header sind damit optimiert, ein erster Schritt: