Websites abdichten
Mehr Sicherheit im Frontend durch HTTP-Header
Die Sicherheit von Webseiten wird von vielen Frontend-Entwicklern gerne als »Backend-Thema« abgeschoben – zu Unrecht, wenn man bedenkt, dass die meisten Angriffe das Einschleusen von Schadcode über das Frontend voraussetzen. Auch wenn es immer Mittel und Wege geben wird, die Sicherheit von Webseiten zu kompromittieren, kann potentiellen Angreifern mit einfachen Handgriffen ihre Arbeit deutlich erschwert werden.
Frontend-Entwickler können ihren Teil dazu beitragen, Web-Projekte sicherer zu machen und vor weit verbreiteten Attacken zu schützen. Nicht immer ist dazu ein Rückgriff auf Backend-Entwickler nötig. In den folgenden beiden Beispielen lässt sich alleine durch einfaches Setzen eines Eintrages in der HTTP-Antwort der Website die Sicherheit deutlich erhöhen. Das gestaltet sich so einfach, dass es eigentlich keine Ausrede gibt, diese nützlichen Features zu ignorieren.
Fall 1 – Ich klicke was, das du nicht siehst: Clickjacking
Clickjacking ist lediglich ein Beispiel für framebasierte Angriffe. Diese Angriffsmethode zielt darauf ab, den User mit einer vermeintlich harmlosen Website interagieren zu lassen, auf der dessen Mausklicks dann aber in Wirklichkeit andere (potentiell schädliche) Aktionen im Hintergrund ausführen. Für einen solchen Angriff wird die Zielseite in einem unsichtbaren iframe geladen.
Der Twitter-Button in iframe 1 dient zur Demo. Er ist anklickbar, hat aber keine echte Twitter-Integration.
iframe 1: Angriffsziel – Den »Folgen«-Button will eigentlich niemand klicken … oder?
iframe 2: Angreifende Webseite
- iframe {
- width: 850px;
- height: 370px;
- position: absolute;
- top: -198px;
- left: -424px;
- /* Nur zur Demonstration auf 50% gesetzt. */
- filter: alpha(opacity=50);
- opacity: 0.5;
- }
Um die Art des Angriffes besser sichtbar zu machen, ist in dem Beispiel die Transparenz noch auf 50% gesetzt (opacity). Bei einem echten Clickjacking-Angriff wird sie aber auf 0% gesetzt, damit ist optisch nicht mehr zu erkennen, was in Wirklichkeit bei einem Klick passiert:
iframe 3: Angreifende Webseite mit vollständig transparentem iframe
Das mag in diesem Fall noch relativ harmlos wirken (Twitter, Facebook & Co. unterbinden Clickjacking schon seit einer Weile), andere Szenarien können durchaus unangenehmer sein:
- Ändern von Privatsphäre-Einstellungen
- Löschen von Accounts oder Daten
- Auslösen von 1-Klick-Bestellungen, o.ä.
In der Vergangenheit gab es auf diese Weise sogar Attacken auf die Einstellungsseite des Flash-Plugins im Browser, um dort unbemerkt die Sicherheitseinstellungen zu ändern.
Wie lässt sich ein solcher Angriff verhindern?
Im Prinzip funktioniert die Abhilfe genau so, wie das »Frame-Busting« vergangener Zeiten:
- if (top.location !== self.locaton) {
- parent.location = self.location;
- }
Es wird also verhindert, dass die eigene Webseite in einem Frame geladen werden kann. Da JavaScript als Sicherheitsmechanismus aber ungeeignet ist, da es leicht umgangen werden kann, verlässt man sich in diesem Fall auf den HTTP Header »X-Frame-Options«. Damit teilt der Server dem Browser mit, ob und von welcher Stelle aus eine Seite in einem iframe eingebettet werden darf.
Dazu gibt es drei unterschiedliche Einstellungen:
- X-Frame-Options: SAMEORIGIN
- Hier greift die Same-Origin-Policy des Browsers und erlaubt die Einbettung nur dann, wenn das Protokoll der Website (http:// oder https://) und die (Sub)Domain der einbettenden und der eingebetteten Seite identisch ist.
- X-Frame-Options: DENY
- Unterbindet das Einbetten der Seite in iframes vollständig
- X-Frame-Options: ALLOW-FROM origin
- Gestattet das Einbetten ausschließlich von einem bestimmten Ziel aus (z.B. http://www.example.com)
Man sollte sich vor dem Setzen dieser Eigenschaft allerdings Gedanken machen, ob es nicht auch Fälle gibt, in denen das Laden der eigenen Website in einem iframe nicht sogar gewünscht sein kann. Das häufigste Beispiel dürfte hierfür etwa die Detailseite der Google Bildersuche sein. Beispiele, wie der Header im eigenen Webserver gesetzt werden kann, finden sich am Ende des Artikels.
Browser, die X-Frame-Options unterstützen
- Chrome 4+
- Firefox 3.69+
- Safari 4+
- Internet Explorer 8+
- Opera 10.50+
Fall 2 – (Un)geladene Gäste: Scripte und Ressourcen von anderen Websites
Mittlerweile laden Websites viele Dinge aus anderen Quellen: Webfonts, JavaScript-Bibliotheken von CDN-Servern, Werbung und Zählpixel, Sharing-Buttons sozialer Netzwerke und vieles mehr. Doch wie können wir sicher sein, dass über diese Kanäle immer das kommt, was wir haben wollen? Was unterscheidet beim Laden eines Scriptes im Browser google-analytics.com von evil.com?
Gar nichts. Und das kann mitunter ein Problem werden – im besten Falle gibt es nur Fehlermeldungen in der Entwickler-Konsole des Browsers, etwas ärgerlicher wird es, wenn dadurch unsere eigene Website nicht mehr korrekt funktioniert. Zu einem echten Problem wird es allerdings, wenn über eine dieser Quellen Schadcode ausgeführt wird, die etwa die Seite betrügerisch manipuliert, den Browser zum Absturz bringt, Cookies oder Session-Daten entführt oder anderweitig den User gefährdet. Man spricht dabei von sogenannten Cross-Site-Scripting-Attacken, oder auch kurz »XSS«. Dabei muss es noch nicht immer JavaScript sein: Auch eventuelle Browser-Fehler bei der Verarbeitung manipulierter Medien (z.B. Bilder, Webfonts, Videos) oder veraltete Plug-ins (Java, Flash-Player) können zu Sicherheitslücken führen.
Um diese Probleme künftig einzudämmen, wurde der HTTP-Header Content-Security-Policy (CSP) eingeführt. Anstatt allem blind zu vertrauen, was von externen Quellen in die Seite geladen wird, können Webworker jetzt explizit eine Whitelist der erlaubten Ressourcen definieren:
- Content-Security-Policy: "script-src 'self' http://code.jquery.com"
script-src
listet die erlaubten Quellen für JavaScript-Dateien auf (getrennt mit Leerzeichen), in diesem Beispiel die aufrufende Seite selbst ('self', ohne Subdomains) und der jQuery-CDN-Server ('code.jquery.com'). Wird nun versucht, ein Script von einer anderen Quelle aus zu laden, erscheint in der Entwicklerkonsole des Browsers eine entsprechende Fehlermeldung.
- <!DOCTYPE html>
- <html>
- <head>
- <script src="http://code.jquery.com/jquery.js"></script>
- <script src="http://evil.com/evil.js"></script>
- </head>
- <body>
- …
- </body>
- </html>
Sollen Inline-Scripte in der HTML-Seite erlaubt werden, muss zusätzlich noch 'unsafe-inline'
angegeben werden.
- Content-Security-Policy: "script-src 'self' 'unsafe-inline' http://code.jquery.com"
Aber Vorsicht: Bei sogenannten »Stored XSS-Attacken« gelingt es Angreifern, Script-Tags etwa in der Datenbank der Website (CMS, Kommentarfunktion, o.ä.) dauerhaft zu speichern, so dass sie bei jedem Seitenaufruf geladen und ausgeführt werden. Inline-Scripten sollte grundsätzlich nur vertraut werden, wenn zusätzlich im Backend für einen XSS-Schutz gesorgt wird.
Darüber hinaus gibt es neben script-src
noch weitere CSP-Regeln, mit denen Inhalte zusätzlich eingeschränkt werden können:
- connect-src: Schränkt ein, zu welchen Servern sich die Seite selbst verbinden kann, etwa per Ajax oder WebSockets.
- font-src: Web-Fonts (etwa TypeKit, Google Webfonts)
- frame-src: Quellen, die per (i)frame in der Seite eingebettet werden dürfen (z.B. YouTube, Vimeo)
- img-src: externe Quellen für Bilder …
- media-src: … Audio/Video …
- object-src: … sowie Flash und andere Plug-Ins.
- style-src: Gegenstück zu
script-src
für Stylesheets.
Wird eine CSP-Regel nicht im Header angegeben, werden Ressourcen von allen Quellen zugelassen. Einzelne Regeln werden mit einem Semikolon von einander getrennt:
- Content-Security-Policy: "script-src 'self' http://example.com; style-src 'self' http://fonts.googleapis.com/; font-src http://themes.googleusercontent.com/"
Bei einem komplexen Webangebot mit vielen externen Ressourcen (z.B. Werbung), empfiehlt es sich, sich zunächst erst mal einen Überblick zu verschaffen, welche Daten von welchen Servern geladen werden. Dazu kann der CSP-Header im »Report-Only-Modus« verwendet werden, es werden also zunächst keine Inhalte vom Browser blockiert, sondern alle externen Inhalte mitgeloggt. Mathias Bynens hat dazu ein kleines Tool in PHP geschrieben.
- Content-Security-Policy-Report-Only: "default-src 'self'; ...; report-uri /csp-reporter.php"
Wer schon einen genaueren Überblick hat, kann die erforderlichen Einstellungen direkt mit dieser Chrome-Extension testweise vornehmen. Da sie direkt auf der Website des Entwicklers zu Verfügung gestellt wird und nicht aus dem Google Web Store kommt, muss die Datei für die Installation heruntergeladen und per Drag & Drop in die Erweiterungsliste von Chrome gezogen werden.
Sind alle erlaubten Quellen zusammengestellt, kann man die Konfiguration nun auf dem eigenen Webserver übernehmen. Gerade wer Werbung auf seiner Website einblendet, sollte nochmal alle Einstellungen genau prüfen und auch gegebenenfalls mit dem Anbieter der Werbeplattform Rücksprache halten, ob auch alle möglichen Werbequellen in die Liste der erlaubten Domains aufgenommen wurden. Gegebenenfalls müssen noch Browser-Extensions berücksichtigt werden, die Scripte von anderen Quellen in eine Website einfügen.
Ist alles nach Wunsch fertig konfiguriert, wurde in modernen Browsern die Angriffsfläche für XSS-Attacken auf die eigene Website mit relativ wenig Aufwand erheblich reduziert.
Browser, die Content-Security-Policy unterstützen:
- Chrome 25
- Firefox 4+ (Versionen kleiner 23 als »X-Content-Security-Policy«)
- Safari 6.1+
- Internet Explorer 10 (eingeschränkt)
Wie setze ich die vorgestellten HTTP Header in meinem Webserver?
Für den meistverwendeten Webserver Apache genügt es, eine .htaccess
-Datei Im Stammverzeichnis abzulegen:
- <IfModule mod_headers.c>
- Header always set X-Frame-Options SAMEORIGIN
- </IfModule>
In der web.config
von Microsofts IIS werden die Einträge im Abschnitt <customHeaders>
hinzugefügt:
- <?xml version="1.0" encoding="UTF-8"?>
- <configuration>
- <system.webServer>
- <httpProtocol>
- <customHeaders>
- <add name="X-Frame-Options" value="SAMEORIGIN" />
- </customHeaders>
- </httpProtocol>
- </system.webServer>
- </configuration>
Zu guter Letzt können die Header in nginx entweder im http
, server
oder location
-Abschnitt der Konfiguration hinzugefügt werden:
- add_header X-Frame-Options SAMEORIGIN;
Fazit
Auch wenn es absolute Sicherheit im Netz nicht gibt: Da heute immer mehr alltägliche Aufgaben online erledigt werden, müssen Webworker stets im Blick behalten, dass sich die Nutzer nicht nur wohl, sondern auch sicher fühlen. Das ist die Verantwortung eines jeden Entwicklers.
Weitere Ressourcen
X-Frame-Options
- Facebook Lacked X-Frame-Options - How I Fixed It
- X-Frame-Options: All about Clickjacking? (Google Docs Artikel)
- RFC7034: HTTP Header Field X-Frame-Options
Content Security Policy
- HTML5 Rocks: An Introduction to Content Security Policy
- Twitter Blog: CSP to the Rescue: Leveraging the Browser for Security
- Mathias Bynens: Processing Content Security Policy violation reports
- CSP Is Awesome: Generate a Content-Security-Policy header
Kommentare
Johannes Braun
am 02.12.2013 - 10:08
Mal ein etwas ungewöhnlicheres aber umso spannenderes - und wichtiges - Thema.
Matthias
am 02.12.2013 - 14:25
.htaccess --> Header always append X-Frame-Options SAMEORIGIN
Frederic Hemberger (Autor)
am 02.12.2013 - 16:43
"set" stimmt schon, "append" geht aber auch.
Olaf Gleba (Webkraut)
am 02.12.2013 - 15:14
Vielen Dank für das extrem nützliche Thema/den Artikel. Ein echtes Stiefmutterthema. Gilt leider auch für mich selber ;-(
Tim Adam
am 02.12.2013 - 15:40
Ich schließe mich den anderen Kommentatoren gerne an. Danke für den detaillierten Artikel.
Silke Capo
am 02.12.2013 - 18:05
Vielen Dank für den spannenden Artikel! Das Thema Sicherheit geht im Alltagsstress ja gerne etwas unter... Werde die Sachen gleich mal ausprobieren :-)
Christoph
am 04.12.2013 - 09:22
Danke für den aufschlußreichen Artikel, Frederic. Wirklich zu einfach, um es nicht zu verwenden.
Michael
am 06.12.2013 - 11:03
Wie sieht es denn bei der Content-Security-Policy in Verbindung mit Browser Extensions aus? Funktionieren Erweiterungen wie Beispielsweise Evernote dann überhaupt noch, die auch JavaScript nachladen?
Frederic Hemberger (Autor)
am 06.12.2013 - 16:23
Da kommen jetzt unterschiedliche Faktoren zusammen:
CSP innerhalb von Browser-Erweiterungen
Google Chrome verwendet z.B. auch innerhalb der aktuellen Extensions eine CSP. Als Default-Wert ist dort
script-src 'self';
gesetzt. Das bedeutet, dass Code wieeval();
odersetTimeout("alert(1)", 1)
nicht funktioniert und von der CSP geblockt wird. Das gleiche gilt für Inline-Scripts (auch wenn sie von der Extension dynamisch in den DOM injected werden). Diese CSP lässt sich in diesem Fall auch explizit nicht mit Einträgen wie'unsafe-inline'
aufweichen.Externe Scripte müssen daher entweder aus dem lokalen Verzeichnis der Extension kommen oder explizit auf eine Whitelist gesetzt werden, wobei als Quellen ausschließlich Ressourcen von https://, chrome-extension:// und chrome-extension-resource:// zugelassen sind. Das soll »Man-in-the-Middle«-Attacken verhindern, die weitere Scripte aus unsicheren Quellen einschleusen könnten. Details dazu findest du unter developer.chrome.com
Wie das bei Firefox und IE ist, kann ich momentan leider nicht sagen.
Extensions, die externe Scripte in den DOM der Seite einfügen
Hier sollten ganz normal die CSP-Einstellungen der Seite greifen.
Man muss außerdem noch beachten, dass es sich bei der Content-Security-Policy um noch einen recht jungen Standard handelt, an dem auch momentan noch weiter gearbeitet wird. Gerade Fragen, welche Browser-Erweiterungen und Bookmarklets angehen, werden momentan noch diskutiert und die Spezifikation verfeinert. Auch wenn sich also Details in der Implementierung in den Browsern noch ändern können, sollte man den zusätzlichen Sicherheitsgewinn trotzdem heute schon in Anspruch nehmen.
Christoph
am 16.12.2013 - 13:55
Danke! Ein sehr interessanter Artikel, kenntnisreich geschrieben.
Frederic Hemberger (Autor)
am 28.12.2013 - 16:17
Kleine Ergänzung noch zu X-Frame-Options: Aktuell unterstützen Chrome, Safari und Opera noch nicht die "allow-from"-Direktive, ein entsprechendes Bugticket dazu gibt es schon für Chrome.
Auf dieser Seite lässt sich der Support im eigenen Browser prüfen.
Die Kommentare sind geschlossen.