Sichere Webserver(konfiguration)
Sichere Konfiguration
Allgemein
Das Ziel ist es einen Webserver so zu konfigurieren, dass laut aktuellen Sicherheitsstandards als sicher gilt. Als Webserver wurde Apache 2.4 und zur HTTP(S)-Header Kontrolle Mozilla Observatory verwendet. Observatory besteht aus (bis zu) drei Komponenten, wobei der Kern und die API essentiell sind und die Weboberfläche optional. Zum Einen existiert eine Onlineversion unter https://observatory.mozilla.org/, zum Anderen können diese Komponenten auch lokal installiert und ausgeführt werden. Eine lokale Installation ist essentiell, falls ein lokaler Webserver geprüft werden soll, der bspw. keinen Zugang zum WWW hat / haben soll oder sich in einem abschotteten Subnetz befindet. Des Weiteren muss der Webserver nicht während der Konfigurationsphase mit dem Internet verbunden sein und so verwundbar sein.
Sicherheitskonzepte
Content Security Policy
Cross-origin Resource Sharing
Der HTTP-Header "Access-Control-Allow-Origin" gibt an, welche anderen Domains auf den Inhalt der eigenen Domain über Scripte zugreifen dürfen. Dabei wird die Same-Origin-Policy <ref>https://de.wikipedia.org/wiki/Same-Origin-Policy</ref> bewusst umgangen, um z.B. über Domain-Grenzen hinweg Web-Applikationen in einer gemeinsamen Oberfläche zu kombinieren. Dieser Header sollte nur vorhanden sein, falls dies unbedingt benötigt wird und sollte auf so wenig Domains wie nötig beschränkt werden. Als Beispiel sollte ein Server, der eine Webseite und eine API betreibt und den Dienst XMLHttpRequest access auf einer Remote-Webseite anbietet, sollte nur die API den Access-Control-Allow-Origin Header senden.<ref>https://wiki.mozilla.org/Security/Guidelines/Web_Security</ref>
Der Zugriff kann durch weitere Weitere Acces-Control-* Header eingeschränkt werden.
# Allow https://random-dashboard.mozilla.org to read the returned results of this API
Access-Control-Allow-Origin: https://random-dashboard.mozilla.org
Access-Control-Allow-Methods: GET
HTTP Public Key Pinning
Bei HTTP Public Key Pinning (HPKP) handelt es sich um eine Maßnahme um sich gegen bösartige Zertifizierungsstellen zu schützen. Das Grundproblem des gesamten Systems der Zertifizierungsstellen: Gängige Browser vertrauen automatisch mehreren hundert Zertifizierungsstellen. Jede einzelne davon kann theoretisch nach Belieben gefälschte Zertifikate für fremde Webseiten ausstellen. Jede Zertifizierungsstelle ist zudem in der Lage, Unterzertifizierungsstellen zu ernennen. Auch diese werden anschließend von Browsern automatisch erkannt.<ref>http://www.golem.de/news/https-zertifikate-key-pinning-schuetzt-vor-boesartigen-zertifizierungsstellen-1410-109799.html</ref> Durch gefälschte Zertifikate werden Man-in-the-Middle-Angriffe <ref>https://de.wikipedia.org/wiki/Man-in-the-Middle-Angriff</ref> auf TLS-Verbindungen möglich.
Hier soll HPKP Abhilfe schaffen. Mittels eines HTTP-Headers kann der Betreiber einer Webseite einen Pin mitschicken. Dabei handelt es sich um Hashes von kryptographischen Schlüsseln und einen Zeitwert. Wenn der Browser das Pinning unterstützt, speichert er diese Information und akzeptiert fortan nur noch Verbindungen, bei denen das Zertifikat einen der gepinnten Schlüssel nutzt.
Public-Key-Pins: pin-sha256="base64=="; max-age=expireTime [;includeSubdomains] [; report-uri="reportURI"]
- Pin-sha256: beinhaltet einen Base64 codierten Subject Public Key Information (SPKI) Fingerprint
- Max-age: Zeit in Sekunden, wie lange der Browser sich die Information merken soll
- includeSubdomains: Regeln auch für Subdomains
- report-uri: Validierungsfehlschläge an die angegebene URL melden
Da die Gefahr besteht, seinen eigenen Server den Nutzern unzugänglich zu machen, falls HPKP falsch implementiert wird, sollte mit äußerster Vorsicht vorgegangen werden. Es sollten Backup-Key-Pins mitgesendet werden. Weiterhin sollte die Option "Public-Key-Pins-Report-Only" zunächst zum testen verwendet werden. Ebenfalls sollten zu Beginn nur sehr kurze "Max-age" Werte verwendet werden.
Da die Gefahr eines "self-denial-of-service" besteht und die Gefahr, dass eine CA falsche Zertifikate liefert eher gering ist, wird empfohlen, dass nur sicherheitskritische Seiten HPKP implementieren sollten.<ref>https://wiki.mozilla.org/Security/Guidelines/Web_Security#HTTP_Public_Key_Pinning</ref>
HTTP Strict Transport Security
Der HTTP Strict Transport Security (HTST) ist ein Header, der dem Client mitteilt, dass sie sich nur auf die Seite über HTTPS verbinden dürfen, selbst wenn der ursprüngliche Aufruf über HTTP angesetzt wurde. Wenn der HTST Header gesetzt ist, werden vom Browser alle Anfragen auf HTTPS ändern. Weiterhin werden Zertifikat-Errors strikter beachtet. Der Nutzer erhält dann nicht mehr die Möglichkeit solche Fehler zu ignorieren und trotzdem auf Seite zu verbinden.
# Only connect to this site and subdomains via HTTPS for the next year and also include in the preload list
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
- max-age: Wie lange die Information gültig ist
- includeSubDomains: HTST sollen vom Browser auch für SubDomains umgesetzt werden
- preload: Die Seite soll in die HSTS preload list hinzugefügt werden
HSTS wird nur für verschlüsselte HTTPS-Verbindungen unterstützt, denn unverschlüsselte HTTP-Verbindungen können von einem Angreifer manipuliert werden, so dass der Anwender den Informationen nicht vollständig vertrauen kann. Der Header wird also beim Ersten Besuch der Seite mitgeliefert. (Trust on first use<ref>https://en.wikipedia.org/wiki/Trust_on_first_use</ref>) Um das zu umgehen, bietet Google einen HSTS preload service.<ref>https://hstspreload.appspot.com/</ref> Wenn die eigene Seite auf der HSTS preload list vorhanden ist, werden alle aktuellen Browser ihre Anfragen über HTTPS senden. <ref>https://developer.mozilla.org/en-US/docs/Web/Security/HTTP_strict_transport_security</ref>
Subresource Integrity
X-Content-Type-Options
Setzt der Server den HTTP-Header X-Content-Type-Options: nosniff, so wird dem Browser signalisiert, dass der MIME-Type in jedem Fall respektiert werden soll. Javascript-Code in Bildern wird damit ignoriert.<ref>http://www.golem.de/news/cross-site-scripting-javascript-code-in-bilder-einbetten-1411-110264-2.html</ref>
Der Browser wird davon abgehalten MIME-sniffing zu versuchen, um den Inhalt des Pakets zu ermitteln. Es muss die im Content-Type Header klassifiziert Information verwendet werden. Falls die Information im Content-Type Header falsch ist, wird der Inhalt vom Browser blockiert.
X-Content-Type-Options: nosniff
X-Frame-Options
X-XSS-Protection
Beispiel Apache Konfiguration
--Debian apache 2.4
-enable mod_headers
sudo a2enmod headers
-insert into config file:
sudo vi /etc/apache2/conf-available/security.conf
-enable XFO
Header set X-Frame-Options: "sameorigin"
-enable CSP
Header set Content-Security-Policy: "object-src 'self'; frame-src 'self'; script-src 'self‘;"
-X-Content-Type-Options
Header set X-Content-Type-Options: „no sniff“
-Config for port 80 (redirect to https)
sudo a2enmod rewrite
-in Virtual Hosts file:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
</IfModule>
-Config for port 443
-enable SSL, e.g.
SSLEngine On
SSLCertificateFile /etc/ssl/localcerts/apache.pem
SSLCertificateKeyFile /etc/ssl/localcerts/apache.key
Mozilla Observatory
Beschreibung
Das Observatory von Mozilla ist ein kostenloses Tool um Webseiten auf ihre Sicherheit zu überprüfen. Damit sollen Webseitenbetreibern die Möglichkeit gegeben werden ihre Seite auf Schwachstellen zu prüfen. Dabei überprüft Observatory ob verschiedene Sicherheitsmechanismen vorhanden und wie gut diese implementiert sind. Damit sollen Entwicklern und System-Administratoren unterstütz werden, ihre Webseite sicher zu implementieren. <ref> https://pokeinthe.io/2016/08/25/observatory-by-mozilla-a-new-tool/ </ref>
Observatory überprüft dabei, ob die Site beispielsweise über Cookie-Secure-Flags verfügt oder die Integrität von Skripten überprüft (Subresource Integrity). Weitere Checks betreffen die Verschlüsselung von Inhalten per HTTPS (Redirections, HSTS, HTTP Public Key Pinning). Überprüft werden zudem die Einstellungen zu Cross-Origin Resource Sharing (CORS) und Content Security Policy (CSP), X-Frame-Optionen, X-Content-Type-Optionen und X-XSS-Protection. Die Auswertung startet bei 100 Punkten. Observatory vergibt dabei eine Wertung von A+ bis F. In den elf überprüften Bereichen können bis zu 50 Punkte abgezogen werden, wenn bestimmte Sicherheitsmerkmale nicht aktiviert oder falsch implementiert sind.<ref> https://pokeinthe.io/2016/08/25/observatory-by-mozilla-a-new-tool/ </ref> Die niedrigste Punktzahl liegt jedoch bei null Punkten.
Der Nutzer erhält vor dem Überprüfen die Option, die Webseite nicht mit in die Liste der öffentlich einsehbaren Webseiten zu übernehmen. Weiterhin wird die Möglichkeit geboten auf Third-party Anbieter zu verzichen. Dabei wird die Möglichkeit geboten, dass die Seite u. a. auf verfügbare cipher suites, handshake methods, supported protocols und Resistenz von TLS-Angriffen zu überprüfen. <ref>https://observatory.mozilla.org/faq.html </ref>
Weiterhin wurde ein Repository mit dem Tool für eine lokale Installation, unter der Mozilla Public License Version 2.0, veröffentlicht. <ref>https://github.com/mozilla/http-observatory</ref> Das Tool besteht aus drei Modulen. Zum einen wird mit "observatory-cli" das offizielle Node.js command line interface mitgeliefert. Des weiteren wird der Scanner und eine API bereitgestellt. Unabhängig davon kann die HTTP Observatory Webseite separat heruntergeladen werden. <ref>https://github.com/mozilla/http-observatory-website</ref>
Mit dem Scanner und der API kann Observatory auch lokal betrieben werden. Damit können Server bereits getestete werden, bevor sie der Öffentlichkeit im Internet zugänglich gemacht werden. Die Daten der getesteten Server liegt dabei in einer lokalen Postgres Datenbank.
Das command line interface kann unabhängig von der lokalen Installation betrieben werden, da es auf das von Mozilla zur Verfügung gestellte Observatory zugreift. Es wird also nicht die lokale Installation verwendet.
Lokale Installation
#how to install observatory (tested on Ubuntu 16.04 LTS)
sudo apt-get install -y git libpq-dev postgresql redis-server python3 python3-pip
cd /opt/
sudo git clone https://github.com/mozilla/http-observatory.git
sudo su - postgres
createdb http_observatory
psql http_observatory < /opt/http-observatory/httpobs/database/schema.sql
psql http_observatory
\password httpobsapi #passwort festlegen z.B. its
\password httpobsscanner #passwort festlegen z.B. its
#exit db (\q)
#exit psql user (exit)
sudo vi /etc/postgresql/9.5/main/postgresql.conf #set max_connections = 512, shared_buffers = 256MB
sudo service postgresql restart
sudo useradd -m httpobs
sudo su - httpobs
cd /opt/http-observatory
pip3 install .
pip3 install -r requirements.txt --upgrade
exit
#everything from here has to be done for every start - Starting from normal user
#start scanner
sudo install -m 750 -o httpobs -g httpobs -d /var/run/httpobs /var/log/httpobs
sudo su - httpobs
echo export HTTPOBS_API_URL="http://localhost:57001/api/v1" >> ~/.profile
cd /opt/http-observatory/
HTTPOBS_DATABASE_USER="httpobsscanner" HTTPOBS_DATABASE_PASS="its" /opt/http-observatory/httpobs/scripts/httpobs-scan-worker
#open new Terminal to start api
sudo su - httpobs
cd /opt/http-observatory/
HTTPOBS_DATABASE_USER="httpobsapi" HTTPOBS_DATABASE_PASS="its" uwsgi --http :57001 --wsgi-file /opt/http-observatory/httpobs/website/main.py --processes 8
--callable app --master
Verwendung der API
- Aufruf zum Scannen einer neuene Seite starten
- POST-Request
- http://localhost:57001/api/v1/analyze?host=www.testseite.de
- hidden=true&rescan=true
- hidden=true&rescan=true
- POST-Request
- Status eines Scans anzeigen
{
"end_time": "Tue, 22 Mar 2016 21:51:41 GMT",
"grade": "A",
"response_headers": { ... },
"scan_id": 1,
"score": 90,
"start_time": "Tue, 22 Mar 2016 21:51:40 GMT",
"state": "FINISHED",
"tests_failed": 2,
"tests_passed": 9,
"tests_quantity": 11
}
<ref>https://github.com/mozilla/http-observatory/blob/master/httpobs/docs/api.md#scan</ref>
- Resultate eines bestimmten Scans anzeigen (Scan-Nr.)
- GET-Request
- http://localhost:57001/api/v1/getScanResults?scan=1
- Beispiel siehe unten
- GET-Request
- Liste Letzter Scans anzeigen
- GET-Request
- http://localhost:57001/api/v1/getRecentScans?max=20
- mit Option von bestimmten "Noten" in diesem Fall alle F, also 20 Punkte und weniger
- mit Option von bestimmten "Noten" in diesem Fall alle F, also 20 Punkte und weniger
- GET-Request
{
"site1.mozilla.org": "A",
"site2.mozilla.org": "B-",
"site3.mozilla.org": "C+",
"site4.mozilla.org": "F",
"site5.mozilla.org": "F",
"site6.mozilla.org": "E",
"site7.mozilla.org": "F",
"site8.mozilla.org": "B+",
"site9.mozilla.org": "A+",
"site0.mozilla.org": "A-"
}
- Liste der möglichen Noten mit Aufteilung getesteter Seiten
{
"A+": 3,
"A": 6,
"A-": 2,
"B+": 8,
"B": 76,
"B-": 79,
"C+": 80,
"C": 88,
"C-": 86,
"D+": 60,
"D": 110,
"D-": 215,
"E": 298,
"F": 46770
}
Beispiel für Resultat eines Scans
The tests object contains one test object for each test conducted by the HTTP Observatory. <ref>https://github.com/mozilla/http-observatory/blob/master/httpobs/docs/api.md#tests</ref> Each test object is contains the following values:
- expectation: the expectation for a test result going in
- name: the name of the test; this should be the same as the parent object's name
- output: artifacts related to the test; these can vary widely between tests and are not guaranteed to be stable over time.
- data: generally as close to the raw output of the test as is possible. For example, in the strict-transport-security test, output -> data contains the raw Strict-Transport-Security header
- ????: other values under output have keys that vary; for example, the strict-transport-security test has a includeSubDomains key that is either set to True or False. Similarly, the redirection test contains a route key that contains an array of the URLs that were redirected to. See example below for more available keys.
- pass: whether the test passed or failed; a test that meets or exceeds the expectation will be marked as passed
- result: result of the test
- score_description: short description describing what result means
- score_modifier: how much the result of the test affected the final score; should range between +5 and -50
{
"content-security-policy": {
"expectation": "csp-implemented-with-no-unsafe",
"name": "content-security-policy",
"output": {
"data": {
"connect-src": [
"'self'",
"https://sentry.prod.mozaws.net"
],
"default-src": [
"'self'"
],
"font-src": [
"'self'",
"https://addons.cdn.mozilla.net"
],
"frame-src": [
"'self'",
"https://ic.paypal.com",
"https://paypal.com",
"https://www.google.com/recaptcha/",
"https://www.paypal.com"
],
"img-src": [
"'self'",
"data:",
"blob:",
"https://www.paypal.com",
"https://ssl.google-analytics.com",
"https://addons.cdn.mozilla.net",
"https://static.addons.mozilla.net",
"https://ssl.gstatic.com/",
"https://sentry.prod.mozaws.net"
],
"media-src": [
"https://videos.cdn.mozilla.net"
],
"object-src": [
"'none'"
],
"report-uri": [
"/__cspreport__"
],
"script-src": [
"'self'",
"https://addons.mozilla.org",
"https://www.paypalobjects.com",
"https://apis.google.com",
"https://www.google.com/recaptcha/",
"https://www.gstatic.com/recaptcha/",
"https://ssl.google-analytics.com",
"https://addons.cdn.mozilla.net"
],
"style-src": [
"'self'",
"'unsafe-inline'",
"https://addons.cdn.mozilla.net"
]
}
},
"pass": false,
"result": "csp-implemented-with-unsafe-inline-in-style-src-only",
"score_description": "Content Security Policy (CSP) implemented with unsafe-inline inside style-src directive",
"score_modifier": -5
},
"contribute": {
"expectation": "contribute-json-with-required-keys",
"name": "contribute",
"output": {
"data": {
"bugs": {
"list": "https://github.com/mozilla/addons-server/issues",
"report": "https://github.com/mozilla/addons-server/issues/new"
},
"description": "Mozilla's official site for add-ons to Mozilla software, such as Firefox, Thunderbird, and SeaMonkey.",
"name": "Olympia",
"participate": {
"docs": "http://addons-server.readthedocs.org/",
"home": "https://wiki.mozilla.org/Add-ons/Contribute/AMO/Code",
"irc": "irc://irc.mozilla.org/#amo",
"irc-contacts": [
"andym",
"cgrebs",
"kumar",
"magopian",
"mstriemer",
"muffinresearch",
"tofumatt"
]
},
"urls": {
"dev": "https://addons-dev.allizom.org/",
"prod": "https://addons.mozilla.org/",
"stage": "https://addons.allizom.org/"
}
}
},
"pass": true,
"result": "contribute-json-with-required-keys",
"score_description": "Contribute.json implemented with the required contact information",
"score_modifier": 0
},
"cookies": {
"expectation": "cookies-secure-with-httponly-sessions",
"name": "cookies",
"output": {
"data": {
"sessionid": {
"domain": ".addons.mozilla.org",
"expires": null,
"httponly": true,
"max-age": null,
"path": "/",
"port": null,
"secure": true
}
}
},
"pass": true,
"result": "cookies-secure-with-httponly-sessions",
"score_description": "All cookies use the Secure flag and all session cookies use the HttpOnly flag",
"score_modifier": 0
},
"cross-origin-resource-sharing": {
"expectation": "cross-origin-resource-sharing-not-implemented",
"name": "cross-origin-resource-sharing",
"output": {
"data": {
"acao": null,
"clientaccesspolicy": null,
"crossdomain": null
}
},
"pass": true,
"result": "cross-origin-resource-sharing-not-implemented",
"score_description": "Content is not visible via cross-origin resource sharing (CORS) files or headers",
"score_modifier": 0
},
"public-key-pinning": {
"expectation": "hpkp-not-implemented",
"name": "public-key-pinning",
"output": {
"data": null,
"includeSubDomains": false,
"max-age": null,
"numPins": null,
"preloaded": false
},
"pass": true,
"result": "hpkp-not-implemented",
"score_description": "HTTP Public Key Pinning (HPKP) header not implemented",
"score_modifier": 0
},
"redirection": {
"expectation": "redirection-to-https",
"name": "redirection",
"output": {
"destination": "https://addons.mozilla.org/en-US/firefox/",
"redirects": true,
"route": [
"http://addons.mozilla.org/",
"https://addons.mozilla.org/",
"https://addons.mozilla.org/en-US/firefox/"
],
"status_code": 200
},
"pass": true,
"result": "redirection-to-https",
"score_description": "Initial redirection is to https on same host, final destination is https",
"score_modifier": 0
},
"strict-transport-security": {
"expectation": "hsts-implemented-max-age-at-least-six-months",
"name": "strict-transport-security",
"output": {
"data": "max-age=31536000",
"includeSubDomains": false,
"max-age": 31536000,
"preload": false,
"preloaded": false
},
"pass": true,
"result": "hsts-implemented-max-age-at-least-six-months",
"score_description": "HTTP Strict Transport Security (HSTS) header set to a minimum of six months (15768000)",
"score_modifier": 0
},
"subresource-integrity": {
"expectation": "sri-implemented-and-external-scripts-loaded-securely",
"name": "subresource-integrity",
"output": {
"data": {
"https://addons.cdn.mozilla.net/static/js/impala-min.js?build=552decc-56eadb2f": {
"crossorigin": null,
"integrity": null
},
"https://addons.cdn.mozilla.net/static/js/preload-min.js?build=552decc-56eadb2f": {
"crossorigin": null,
"integrity": null
}
}
},
"pass": false,
"result": "sri-not-implemented-but-external-scripts-loaded-securely",
"score_description": "Subresource Integrity (SRI) not implemented, but all external scripts are loaded over https",
"score_modifier": -5
},
"x-content-type-options": {
"expectation": "x-content-type-options-nosniff",
"name": "x-content-type-options",
"output": {
"data": "nosniff"
},
"pass": true,
"result": "x-content-type-options-nosniff",
"score_description": "X-Content-Type-Options header set to \"nosniff\"",
"score_modifier": 0
},
"x-frame-options": {
"expectation": "x-frame-options-sameorigin-or-deny",
"name": "x-frame-options",
"output": {
"data": "DENY"
},
"pass": true,
"result": "x-frame-options-sameorigin-or-deny",
"score_description": "X-Frame-Options (XFO) header set to SAMEORIGIN or DENY",
"score_modifier": 0
},
"x-xss-protection": {
"expectation": "x-xss-protection-1-mode-block",
"name": "x-xss-protection",
"output": {
"data": "1; mode=block"
},
"pass": true,
"result": "x-xss-protection-enabled-mode-block",
"score_description": "X-XSS-Protection header set to \"1; mode=block\"",
"score_modifier": 0
}
}
<ref>https://github.com/mozilla/http-observatory/blob/master/httpobs/docs/api.md#tests</ref>
Literatur
<references />