Website-Sicherheit mit HTTP Security-Header erhöhen
Der Einsatz von HTTP Security-Headern ist wichtig um die Sicherheit von Websites zu steigern und um sich vor Hacker-Angriffen zu schützen.
Mit Hilfe von HTTP Security-Headern lässt sich die Sicherheit einer Website deutlich verbessern. Es gibt verschiedene HTTP Security-Header, die wir hier im Einzelnen erklären wollen. Einige dieser Header sind sehr einfach in eine Website einzubinden, andere sind komplexer und benötigen etwas mehr Konfigurationsarbeit. Wir zeigen, wie man für Websites die HTTP Security-Header aktiviert und konfiguriert. Dabei gehen wir unter anderem auf die Integration der Header in WordPress ein, beschreiben aber auch den Weg ohne WordPress.
Was sind HTTP Security-Header?
Fordert ein Browser eine Seite von einem Webserver an, dann antwortet der Webserver mit der Auslieferung der Seite und sendet außerdem einen HTTP-Response-Header mit dem Inhalt.
Neben Metadaten wie Zeichensätzen und Fehlercodes können auch sicherheitsrelevante Einstellungen gesendet werden. Diese schreiben dem Browser vor, wie er sich zu verhalten hat. Sicherheit ist leider ein gern vernachlässigtes Thema bei vielen Website-Betreibern. Man sollte sich unbedingt mit dieser Thematik beschäftigen, wenn man nicht Opfer einer Hacker-Attacke werden möchte. Über die HTTP Security-Header kann man dies oftmals recht einfach umsetzen.
Welche Header gibt es und was können sie?
X-Frame-Options (XFO)
Dieser Header verhindert, dass die Seite in einen Frame in einer anderen Seite geladen werden kann. Nicht unbedingt für die eigene Sicherheit, aber was Content-Diebstahl, Missbrauch etc. betrifft, sicher sehr hilfreich. Mit dem Wert »deny
« wird keinerlei Laden in einem Frame zugelassen, mit »sameorigin
« nur »in sich selbst«.
X-Frame-Options: DENY
X-Content-Type-Options
Dem Browser wird normalerweise mitgeteilt, welche Dateiformate er jeweils laden soll, also z. B. HTML oder JPG. Wenn das nicht angegeben wird, soll der Browser »raten«. Das kann man ausnutzen und so andere Formate einschleusen, die Schaden anrichten können. Mit dem Setzen dieses Headers untersagt man dem Browser das »sniffen« und unterbindet diese Angriffsmöglichkeit.
X-Content-Type-Options: nosniff
HTTP Strict-Transport-Security (HSTS)
Dieser Header informiert den Browser, dass die Seite nur per HTTPS aufgerufen werden darf. Voraussetzung ist hierbei natürlich, dass die Seite über ein SSL-Zertifikat verfügt.
Über den Parameter »max-age
« wird die Zeit (in Sekunden) angegeben, in der der Browser sich merken soll, das die Seite nur über HTTPS aufgerufen werden darf. Mit »includeSubdomains
« werden alle Subdomains mit in diese Regel eingeschlossen.
Strict-Transport-Security: max-age=<expire-time>; includeSubdomains
Referrer-Policy
Dieser Header steuert, ob der Referrer-Wert bei ausgehenden Links übergeben werden darf. Das heißt, darf der Browser einer Seite mitteilen, von welcher Seite der Benutzer gekommen ist. Mit dem Wert »no-referrer
« verbietet man das komplett.
Dieser Header ist nicht direkt für die »Sicherheit« der Website verantwortlich aber in Bezug auf den Datenschutz schon interessant.
Referrer-Policy: no-referrer
Permissions-Policy
Der Permissions-Policy-Header hieß früher »Feature-Policy« und wurde im Mai 2020 umbenannt. Auch die Syntax für die Header-Werte hat sich geändert.
Dieser Header hat nicht so sehr die Sicherheit der Website oder der Betreiber im Mittelpunkt, sondern vielmehr die Datensicherheit des Besuchers. In heutigen Zeiten ein Feature, das man nicht hoch genug einschätzen kann. Man kann den Einsatz von Mikrofon oder Kamera, den Abruf des Standorts, die Nutzung der Vibrationsfunktion u. a. unterbinden oder zulassen.
Permissions-Policy: <directive>=(<allowlist>), <directive>=(<allowlist>)
Content-Security-Policy (CSP)
Dieser Security-Header umfasst viele Konfigurationsmöglichkeiten. Mit der Content-Security-Policy kann man detailliert festlegen, welche Inhaltstypen überhaupt ausgeliefert werden dürfen und von welchen Quellen. Das macht ihn so mächtig, dass man beim Setzen der Parameter sehr aufpassen sollte. Hier ist es ausschlaggebend, welche konkreten Rahmenbedingungen für eine Website gelten.
Soll die Website beispielsweise nur eigene Scripte und Scripte von Google erlauben, muss man dem Parameter »script-src
« die Werte »self
« und »https://www.google.com
« zuweisen. Nach diesem Muster ordnet man allen weiteren Parametern ebenfalls die Werte zu. Eine Liste aller Parameter findet ihr hier.
Content-Security-Policy: default-src 'self'; script-src 'self' https://www.google.com; img-src 'self'; style-src 'self'; font-src 'self'; object-src 'none'; frame-src 'self'; worker-src 'self'; connect-src 'self';
X-XSS-Protection
Dieser Header verhindert das bekannte »Cross Site Scripting«, das gefürchtete Laden von fremden Scripten in die Website. Damit wird ggf. bösartiges JavaScript im Browser ausgeführt, ohne dass der Benutzer es überhaupt mitbekommt. Allerdings verursachte der Eintrag Sicherheits- und Datenschutz-Probleme, weshalb er von aktuellen Browsern gar nicht mehr unterstütz wird. Der Eintrag sollte daher auf „0“ gesetzt oder gleich ganz entfernt werden. Mit einer sauber eingerichteten Content Security Policy ist der X-XSS-Protection Header ohnehin überflüssig.
Security Report erhalten
Durch den Einsatz des Content-Security-Policy Headers erstellt man eine Whitelist für alle erlaubten Quellen. Da dies sehr komplex ist, kann es durchaus passieren, dass man anfänglich nicht alle Quellen erfasst hat. Hierbei ist es hilfreich, einen »Security Report« einzurichten, der Informationen über geblockte Inhalte erfasst und in ein Log-File schreibt. Zusätzlich besteht die Möglichkeit, diese Informationen auch via E-Mail zu versenden, sobald ein »Problem« auftritt.
Content-Security-Policy: report-uri /security-report.php
Zum Erstellen dieses Security-Reports muss folgender Code in eine PHP-Datei übernommen werden und im Verzeichnis eurer Website abgelegt werden. Der Pfad zur Datei wird dann als Endpunkt hinter dem Parameter »report-uri
« an den Content-Security-Policy Header angehangen.
<?php
$log_file = dirname(__FILE__) . '/csp-violations.log';
$log_file_size_limit = 1000000; // bytes
$email_address = 'email@domain.dlt';
$email_subject = 'Content-Security-Policy violation';
$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');
if ($json_data = json_decode($json_data)) {
$json_data = json_encode($json_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
if (!file_exists($log_file)) {
// Send an email
$message = "The following Content-Security-Policy violation occurred on " .
$current_domain . ":nn" .
$json_data .
"nnFurther CSP violations will be logged to the following log file, but no further email notifications will be sent until this log file is deleted:nn" .
$log_file;
mail($email_address, $email_subject, $message,
'Content-Type: text/plain;charset=utf-8');
} else if (filesize($log_file) > $log_file_size_limit) {
exit(0);
}
file_put_contents($log_file, $json_data, FILE_APPEND | LOCK_EX);
}
?>
Integration der HTTP Security Header über die .htaccess
Die Integration der Security Header wird standardmäßig über die .htaccess
durchgeführt. Das folgende Snippet ist ein Beispiel für diese Integration.
<IfModule mod_headers.c>
Header always set X-Frame-Options "deny"
Header always set X-Content-Type-Options "nosniff"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set Referrer-Policy "no-referrer"
Header always set Permissions-Policy "accelerometer=()‚ autoplay=(self), camera=(), encrypted-media=(), fullscreen=(), geolocation=(self), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(self), usb=()"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://matomo.org; img-src 'self'; style-src 'self'; font-src 'self'; object-src 'none'; frame-src 'self'; worker-src 'self'; connect-src 'self'; report-uri /security-report.php"
</IfModule>
Integration der HTTP Security Header über WordPress-Hook
Wenn es sich bei der Website um eine WordPress-Umsetzung handelt, können die Security Header mit dem Filter »wp_headers
« über die functions.php
integriert werden.
if (!empty($_SERVER['HTTPS'])) {
function kb_add_security_headers($headers) {
$headers["x-frame-options"] = "deny";
$headers["x-content-type-options"] = "nosniff";
$headers["strict-transport-security"] = "max-age=31536000; includeSubDomains";
$headers["referrer-policy"] = "no-referrer";
$headers["permissions-policy"] = "accelerometer=(), autoplay=(self), camera=(), encrypted-media=(), fullscreen=(), geolocation=(self), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(self), usb=()";
$headers["content-security-policy"] = "default-src 'self'; script-src 'self' https://matomo.org; img-src 'self'; style-src 'self'; font-src 'self'; object-src 'none'; frame-src 'self'; worker-src 'self'; connect-src 'self'; report-uri ".esc_url(get_template_directory_uri())."/security-report.php";
return $headers;
}
add_filter('wp_headers', 'kb_add_security_headers');
}
Testen der HTTP Security Header
Nach der Integration empfiehlt es sich, die Header auf ihre Funktionsfähigkeit zu testen. Hierfür gibt es einige Tools im Internet.
Hallo
Vielen Dank für diesen sehr informativen Beitrag. Ich beschäftige mich gerade mit der CSP. Bei Mozilla steht, dass report-uri eingestellt wird und man stattdessen report-to verwenden soll.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-uri
report-to wird leider noch nicht von Firefox selbst unterstützt aber von allen anderen Browsern.
Vielleicht gibts dazu auch eine schöne Anleitung von euch?
X-XSS-Protection sollte nicht mehr genutzt werden, wurde auch bereits in einigen Browsern entfernt. Das schützte nur kaum, war leicht zu umgehen und verursachte neue Sicherheits- und Datenschutz-Probleme.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
https://caniuse.com/mdn-http_headers_x-xss-protection
Bei „Header set …“ würde ich in der Regel „Header always set …“ empfehlen, damit das auch bei anderen Statuscodes als 200 gesetzt wird (bzw. bei Fehlercodes wie 500, 40x, …).
Hallo Daniel, vielen Dank für den wertvollen Hinweis! Wir haben den Artikel entsprechend angepasst.
Ich denke Google hat das ganze FloC Experiment eingestampft:
https://www.heise.de/news/Cookie-Nachfolge-Google-beerdigt-FLoC-6337936.html
Hier sollte man vll. den entsprechenden Header wieder raus nehmen…
Hallo Tim, besten Dank für den Hinweis! Wir haben den Eintrag aktualisiert.
Hallo und Danke schön!
Endlich eine aktuelle, leicht verständliche, kurze deutsche Zusammenfassung zu diesem Thema auch für weniger Erfahrene.
HTTP Security Header sind natürlich etwas feines für HTTP und HTTPS.
Nur wie verhält es sich, wenn eine Website auch einmal anderes, z.B. JSON oder XMLNS Daten ausliefert?
Derzeit klotze ich die Security Header in alle Antworten, vom ‚Authorization Required‘ über HTTP(S), SVG-Bildern bis natürlich JSON und XML(NS).
Hi Jonas,
Danke für diese Anleitung!
Ich hab lange gesucht zu dem Thema eine deutsche Anleitung zu finden, denn Englisch & Nerd-Begriffe – da steige ich bei diesem komplexen Thema leider aus.
Mit deiner Hilfe trau mich jetzt endlich mal an das Thema ran, nur vor der „Permissions-Policy“ und der „Content-Security-Policy (CSP)“ hab ich echt noch Respekt, da muss ich erstmal rausfinden, was auf meiner Seite eigentlich alles so geladen wird bzw. läuft…
Kennst Du ein einfaches Tool für die Analyse? Das in Chrome eingebaute hilft mir leider nicht so wirklich, ich verstehe oft einfach nicht, was es mir sagen will.
Danke auf jeden Fall für den Anschub, zwischen den Jahren hab ich damit jetzt was zu basteln :-)
Lieben Gruß & Frohe Weihnachten
Heike
Hallo Heike, vielen Dank für dein Feedback. Ich habe selbst allerdings tatsächlich recht wenig Anteil an diesem Team-Artikel. Ich gebe das Lob aber gerne an meinen Kollegen Robert weiter :) Wie im Abschnitt »Security Report erhalten« beschrieben, kannst du es so konfigurieren, dass du eine Mail erhältst, sobald etwas erstmalig geblockt wird. So merkst du recht schnell, wenn etwas vergessen wurde. Einen Scanner o.ä. kenne ich nicht.
Hi Jonas,
das ging jetzt fix :-)
Ich hab mir für die ersten Versuche jetzt lieber mal ein Plugin (https://wordpress.org/plugins/http-headers/) installiert – da kann ich das einfacher steuern und muss nicht selbst im Code rumwühlen. Im Plugin ist zumindest eine Testfunktion enthalten was die Content-Security-Policy (CSP) angeht und man kann Stück für Stück schauen, was für Fehler ausgegeben werden.
Sobald ich mich sicherer fühle, werde ich den Code (inkl. der Mailfunktion) dann direkt in die htaccess einbauen…
Viele Grüße
Heike
Ganz herzlichen Dank, dass Du Dein Wissen großzügig und vor allem immer verständlich aufbereitet hier teilst. Seit ich zwei kleine Firmen-Websites betreue, bin ich dankbar für jeden Hinweis zur Verbesserung der Sicherheit.
Beim Ausprobieren des WordPress-Codes ist mir aufgefallen, dass wohl noch ein kleiner Tippfehler enthalten ist? In der Zeile
$headers[„content-security-policy“] = „“default-src
ist hinter dem Gleichheitszeichen ein Anführungszeichen zuviel.
Moin,
vielen Dank für einen weiteren tollen Beitrag.
Diesmal ist mir aber ein kleiner Schreibfehler aufgefallen und zwar im report-uri Script. Im Teil für den E-Mailtext steht: „Further CPS violations“, das müsste aber CSP heißen. Zugegeben, ist wirklich nur eine Winzigkeit. ;)
Vielen Dank – wir haben den Typo korrigiert.
Hallo Torben, Danke für den Hinweis! Ist geändert.