Skip to content

HTTP Header und Browser Sicherheit

Web Anwendungen werden immer essentieller und es zeigen sich immer mehr Schwachstellen und Risiken. Als Web Application Entwickler kann man einige Technologien verwenden (die mehr oder weniger verbreitet sind) um das Risiko zu reduzieren. Hier möchte ich Funktionen sammeln die als "neue" HTTP Header bereitgestellt werden:

Content-Security-Policy: (CSP) Dieser HTML5 Header erlaubt es dem Web Server der eine HTML Seite ausliefert Beschränkungen für den html/javascript code zu definieren in Bezug auf Quellen für Code (und andere Ressourcen wie Fonts und Bilder). Sinnvoll ist dieses Verfahren insbesondere in Verbindung mit Web Seiten die keinen Inline JavaScript code enthalten (nur script tags) sowie auf eval in JavaScript verzichten. Eine gute Zusammenfassung findet sich auf html5rocks.com. Leider ist die Unterstützung in den Browsern noch eingeschränkt und die verschiedenen Engines benutzen noch nicht den offiziellen Header name. [W3C Working Draft Content Security Policy 1.0]

Strict-Transport-Security: (HSTS) Ein Problem mit https (SSL/TLS) ist, wenn es nicht oder falsch verwendet wird. Mit dem HSTS header wurde eine Möglichkeit geschaffen dass Web-Seiten dem Browser der User dazu zwingen können für bestimmte URLs nur die SSL Variante zuzulassen. Der Nutzen ist ein wenig umstritten, da es zum einen noch das Henne und Ei Problem gibt und zum Anderen der Header nicht vor man-in-the-middle (MITM) Angriffen oder Phisching Angriffen mittels alternativen URLs schützen kann. Es ist aber trotzdem eine gute Idee den Header einzusetzen, vor allem wenn die Browser Hersteller dann eigene Listen erstellen und diese fest mit der Software ausliefern (und damit das Henne und Ei Problem umgehen). Die OWASP Initiative hat weiterführende Informationen. [IETF WEBSEC Draft: HTTP Strict Transport Security]

Access-Control-Allow-Origin: (CORS) Die Access-Control-Allow Header sind genau genommen kein Mechanismus um die Möglichkeiten einer Web Seite zu beschränken, sondern um diese feingranular um Zugriffe auf fremde Server (die dem zustimmen) zu erweitern. Dies erlaubt unter anderem die Vermeidung des eher problematischen JSON-P Verfahrens (das auf JavaScript eval basiert). Eine gute Beschreibung wie das Verfahren eingesetzt werden kann findet sich bei html5rocks.com. [W3C Working Draft Cross Origin Resource Sharing]

Cache-Control: no-store Diese Einstellung bietet sich vor allem für dynamisch erstellte HTML Seiten an die sensible Daten enthaten die nicht permanent auf dem Client Rechner gespeichert werden sollen. Es sollte nicht für statische ressourcen (Bilder) gesetzt werden, da dies Geschwindigkeit der Web Anwendung durch ständiges neu-übertragen eben solcher verschlechtert. [W3C RFC2616 HTTP/1.1]

Cookies: HttpOnly secure Cookies werden in Web Anwendungen unter anderem dazu eingesetzt Einstellungen oder Details auf dem Client zu erinnern oder eine Session ID zu speichern. Damit sind Cookies eine Achillesferse jedes Authorisationssystems und bedürfen besonderem Schutz (vor Einsichtname oder Auslesen). Wenn ein Server ein Cookie mittels Header anlegt, so kann er diesen Attribute mitgeben die zum einen regeln wann das Cookie zurückgeschickt wird (secure = nur wenn https methode verwendet wird) und zum anderen ob das Cookie mittels JavaScript/DOM ausgelesen werden kann, oder einfach nur für alle weiteren Zugriffe bereitsteht. Web Anwendungen sollten zum Schutz der Session Information unbedingt dieses Verfahren erzwingen: session cookies mit HttpOnly Attribut. [IETF RFC6255 HTTP State Management Mechanism] X-Frame-Option: Erlaubt Web Seiten die nicht in Frames eingesperrt werden dürfen. Dies hat den Vorteil dass es Angreifern etwas schwerer gemacht wird eine Webseite anzuzeigen die normale Funktionalitäten bereitstellt, der Angreifer aber Clicks und Eingaben abfangen kann. Außerdem bietet es auch Schutz Trittbrettfahrern die Ihre eigenen Vorstellungen von Betrieb eines Services haben. (IE8 Security Blog: Clickjacking Defense) [Quelle?]

Server:/X-Powered-By:/Via: Um es (automatisierten) Angreifern nicht so einfach zu machen sollten Header die eine schnelle Identifikation (Fingerprinting) der eingesetzten Software Version erlauben entfernt (im Fall von X-Powered-By: was gerne von JSP compilern erstellt wird) werden. Der Punkt ist eher umstritten: es reduziert die Gefahr dass bekannte Lücken einfach ausprobiert werden nicht, dafür erschwert es den Administrator und Partnern die Überprüfung der Konfiguration. Da aber viele Assessment Tools das Vorhandensein der Header anmerken ist es für einen Softwareersteller ratsam diese Header (und Versionsstrings in Fehlerseiten) konfigurierbar zu machen. In dem zusammenhang ist die Empfehlung einen falschen Versionsstring zurückzuliefern sehr kritisch zu betrachten, lieber einen unkonkreten generischen Header. Z.b. gibt der Apache httpd nur "Server: Apache" aus, wenn "ServerTokens ProductOnly" gesetzt wird. [W3C RFC2616 HTTP/1.1] Übrigens haben all diese Mechanismen bekannte (und unbekannte) Bugs in verschiedenen Browser Versionen. Nicht alle Browser unterstützen die Header im gleichen Funktionsumfang oder nach der gleichen Methode. Es macht dennoch Sinn diese einzusetzen da die Sicherheit von Web Anwendungen für größere Benutzergruppen dennoch verbessert wird. Zudem achten viele (automatisierten) Audits auf das Vorhandensein, so lässt sich mit geringem Aufwand bei der Anwendungserstelllung eine bessere Compliance erreichen.

Update 2012-11-08: Server header hinzugefügt, IE8 Security Blog artikel verlinked der X-Frame-Option erklärt.

TODO 2012-11-23: Es gibt noch die /crossdomain.xml policy files die von Flash und wohl auch dem Java Plugin beachtet werden. Diese Technologie kennt den X-Permitted-Cross-Domain-Policies: header. Dieser fehlt im Artikel noch.

Update 2014-02-16: Google setzt den X-XSS-Protection:"1; mode=block" header, welcher vom IE8 verstanden wird.

Oracle Java SE (hotspot) GC Logfile rotation

Zufällig bin ich bei den Java Flags über die Option -XX:GCLogFileSize=x gestolpert. Da ich bei Oracle (im Gegensatz zur IBM JVM) einige Diagnosefunktionen, wie z.b. rollierende GC logfiles vermisste habe ich weiter gesucht, und den RFE 6941823 gefunden, der (für Java 7U2+) beschreibt, dass das Feature jetzt vorhanden ist, und (umständlich) mit 3 Optionen konfiguriert werden muss:
C:> java -XX:+PrintFlagsFinal -version | find "GCLog"
    uintx GCLogFileSize                              = 0               {product}
    uintx NumberOfGCLogFiles                         = 0               {product}
     bool UseGCLogFileRotation                       = false           {product}
java version "1.7.0_07"
Java(TM) SE Runtime Environment (build 1.7.0_07-b10)
Java HotSpot(TM) 64-Bit Server VM (build 23.3-b01, mixed mode)
Ich würde das ganze immer zusammen mit der Details und Datestamp Option verwenden:
java  -Xloggc:log/app.vgc
      -XX:GCLogFileSize=10M -XX:NumberOfGCLogFiles=10 -XX:+UseGCLogFileRotation
      -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:-PrintGCTimeStamps
Bei IBM kann man das übrigens dank kompaktem Syntax und Variablenexpansion deutlich besser machen:
$ java -Xverbosegclog:app.%Y%m%d.%H%M%S.%pid.vgc,10,10000
Und dazu liefert IBM sogar noch ein Handbuch...

In-Memory Data Management (openHPI)

Hmm.. heute kommt ein SAP Newsletter mit einem Hinweis auf einen 6 wöchigen Online Kurs am HPI (im Rahmen der openHPI Initiative) über das In-Memory Data Management. Der Kurs hat zwar schon vor 2 Tagen angefangen, aber ich schreibe mich mal dennoch ein. Keine Ahnung warum der Newsletter so spät rausging. Aber einen Kurs mit Hasso himself kann man sich doch nicht entgehen lassen, oder? Das ganze ist natürlich nur eine 25h Aktion, aber vielleicht ja dennoch interessant, ich werde hier berichten.

Oracle JDBC Ping Database

Oracle hat im JDBC Treiber (und dem eigenen Connection Pool) jede Menge Funktionen eingebaut, die über das Verhalten von JDBC hinausgehen. Oftmals wird dies mit Performance-Vorteilen begründet. Man sollte diese Feature (nicht nur aus Kompatibilitätsgründen) nicht ungeprüft übernehmen, zum Beispiel hat JBoss AS einen OracleValidConnectionChecker, der mittels Relfection die Oracle Spezialfunktion pingDatabase() verwendet. Alternativ gibt es eine generische Implementierung, die einfach ein Statement absetzt. Die Prüfung ob eine DB connection noch funktioniert (entweder regelmässig im Hintergrund oder bei jedem ausleihen aus dem Pool) ist recht wichtig für einen stabilen Systembetrieb - aber auch sehr Performancerelevant. Mit pingDatabase würde man so vorgehen: Wenn man aber die Implementierung analysiert, so stellt man schnell fest, dass die aktuelle Version des Treibers hier nicht sonderlich effizient implementiert ist. Es wird das SQL Statement "SELECT 'X' FROM DUAL" abgesetzt. (Immerhin wird dazu vorher der Rückgabetyp deklariert: ((OracleStatement)stmt).defineColumnType (1, Types.CHAR, 1);) Es wird kein prepared statement eingesetzt, dass heisst der Datenbank Server muss bei jedem Aufruf das Statement mühsam parsen. Wenn ein Timeout angegeben wurde, so startet der JDBC Treiber bei jedem Ping einen neuen Thread. Auf die timeout Funktion sollte also auf jeden Fall verzichtet werden (Oracle hat diese Variante auch deprecated).

S9Y Syntax Highliter

jippie endlich: ... was gefunden für das dpSyntaxHighliter in S9Y. Und weil die Doku nicht beschreibt wie man es benutzt, hier ein Beispiel:

Resetting TPM 1.2 with Windows Powershell

tpm.msc screenshotMit Hilfe des TPM kann auf moderner Hardware auf sichere Art und Weise Verschlüsselungmaterial abgelegt werden oder die idendität des Gerätes nachgewiesen zu werden. Windows ab Vista und 2008 Server unterstützt dies auch für Bitlocker Festplattenverschlüsselung. Aber das TPM kann auch für andere Anwendungen genutzt werden. Verwaltet wird es mit dem Management Consolen Plugin "tpm.msc". Die meisten Funktionen wie das übernehmen des Besitzes oder das Erzeugen von Keys kann aber über die Windows Management Instrumentation (WMI) auch gespcriptet werden. Dabei bietet sich insbesondere die Powershell an: Wenn man das Owner Passwort hat, so kann man das TPM auch mit der Powershell zurücksetzen: Wenn das TPM dies nicht zulässt, oder das Owner Passwort nicht bekannt ist, so ist es auch möglich über den Nachweis einer physischen Präsenz das TPM zu initialisieren oder zurückzusetzen. screenshot BIOSDie Methode SetPhysicalPresenceRequest kann dazu mit dem Wert 5 (zurücksetzen) aufgerufen werden. Und GetPhysicalPresenceTransition liefert dann die Aktion die der Benutzer durchführen muss. Im Fall des Wertes 2 wäre dies ein Soft-Reboot und eine Bestätigung der Aktion im BIOS. Und wie erwarten funktioniert dies auch (ohne das Owner Passwort abzufragen). Getestet habe ich dies unter Windows 7 auf einem Dell Latitude E6510 mit einem TPM von Broadcomm.

GUI Design

Gerade musste ich über diesen Dialog schmunzeln (es handelt sich dabei um den ClickOne .Net Installer von GitHubForWindows 1.0). Screenshot InstallerDass der Hilfetext abgeschnitten wird ist wohl ein Problem der deutschen Übersetzung in Zusammenhang mit unterschiedlichen Bildschirmauflösungen. Aber warum dieser Hinweistext überhaupt angezeigt wird ist ja schon fraglich. Der nimmt deutlich mehr Platz ein als jemals ein Domain Name lang sein könnte.

Passwortsicherheit

Passwörter haben eine Menge Probleme. Deswegen gibt es jede Menge best-practice um diese Probleme etwas zu reduzieren. Eine einfache Regel ist, dass man Passwörter von Benutzern so abspeichern sollte, dass diese nicht wiederhergestellt werden können. Der Grund dafür ist insbesondere dass die Passwörter die ggf. in unterschiedlichen Diensten genutzt werden nicht eventuellen Hackern (oder unehrlichen Administratoren) in die Hände fallen können. Dazu werden die Passwörter durch eine Hash Funktion einwegverschlüsselt. Bei der Prüfung des Passwortes wird dann nicht das eingegebene Passwort mit der gespeicherten Version vergleichen, sondern die Einweg-Funktion darauf angewendet und dann verglichen. Eine Konsequenz dieses Verfahren ist es, dass das Passwort von der Anwendung nicht wieder angezeigt oder per Mail an den User gesendet werden kann. Bei mehr oder weniger öffentlichen Diensten ist es üblich, dass es dort eine Passwort-vergessen Funktion gibt die eine Reset Mail an den Benutzer versendet. Dieses Verfahren verlässt sich auf die Sicherheit des E-Mail Verkehrs - ist somit keine sonderlich sichere Option - aber bei Diensten die mehr oder weniger öffentlich angeboten werden ist die E-Mail Adresse sowieso die einzige zusätzliche Möglichkeit den User zu erreichen. Es gibt im wesentlichen 3 Möglichkeiten für solche Passwort-Zurücksetzungsmails: a) aktuelles Passwort per E-Mail zusenden. Das hat den ganz großen Nachteil, dass das Passwort dazu wiederhergestellt werden muss (also nicht Einweg-verschlüsselt sein kann). Außerdem wird das aktuelle Passwort unverschlüsselt per E-Mail versendet. Was besonders problematisch ist wenn dieses wiederverwendet wurde oder weiterverwendet wird. b) neues Passwort erzeugen und zumailen. Nachteil bei dieser Methode: jeder kann eine beliebige E-Mail Adresse eingeben um deren Besitzer mit nicht funktionierenden Passwörtern zu nerven. c) Reset-Link erzeugen und zumailen. Wenn der Benutzer den Link benutzt, so wird er um Eingabe eines neues Passwortes gebeten. Dies bietet den Vorteil, dass das alte Passwort weiterhin gültig bleibt wenn die Funktion jemand unberechtigt benutzt. Außerdem wird kein Passwort per (unverschlüsselter) Mail versendet. Man sieht hier schnell, dass die Methode a) nicht nur die unsicherste und unpraktische ist, sondern auch dass man daraus auch als Endanwender (ohne den Quelltext zu analysieren oder die Datenbank zu kennen) sofort ablesen kann, dass die Web Anwendung eine unsichere Passwort Speicherung verwendet. Immer wenn ich eine Passwort Recovery Mail mit einem bestehenden Passwort bekomme regt mich das auf, deswegen muss ich jetzt mal einfach hier im Blog "Fingerpointint" betreiben:
  • Intel Software Network: speichert und mailt Passwörter an Benutzer
  • BMC Servicedesk Express: speichert und mailt Passörter an Benutzer. (Beispiel)
  • Open Application Group (OAGi) Portal. (Beispiel)
  • RosettaNet.org (Beispiel)
  • kontent.de (Beispiel)
  • mailman Die GNU Mailing-List-Manager-Software weist wenigstens beim Anlegen eines Kontos schon drauf hin (einfacher wäre es die Eingabe generell zu entfernen und nur zufällige Passwörter zuzusenden)
Update: OAGi und Rosettanet.org (danke Christian) hinzugefügt. kontent.de hinzugefügt (danke Robin). Added mailman.

E-Mail versenden von Amazon EC2 (SES mit Exim)

Wie ich bereits berichtet hatte kann es für einen Anwender der IaaS Cloud Amazon EC2 Sinn ergeben den Simple Email Service (SES) von Amazon zu verwenden um E-Mails zu versenden. Dabei kann man entweder die Amazon API/Tools verwenden, oder inzwischen auch die Mails per smtps an den Amazon Smarthost liefern. Amazon selbst bietet nur einen SMTP-SSL Server auf Port 465 an. Die meisten MTAs können dieses Protokoll nicht mehr sprechen und ziehen STARTTLS vor, deswegen empfiehlt Amazon in seiner Anleitung die Verwendung von stunnel als zwischengeschaltenen Proxy. Diese Indirektion ist aber unschön, und kann mit einer aktuellen Exim Version vermieden werden. Ab der Version 4.77 unterstützt der SMTP Transport von Exim auch das smtps Protokoll (für ausgehende Verbindungen). Mit folgenden Exim Einstellungen (Auszug) kann also ein EC2 Linux Rechner direkt an den Amazon Dienst die E-Mail ausliefern (wichtig: die Envelop-From und From: Addresse der versendeten Mails müssen in der Liste der verifizierten SES Absender sein. Als Empfänger kommen nur dann beliebige E-Mail Addressen in Frage, wenn man diese Funktion bei Amazon explizit freischalten hat lassen. 1. Alle Mails an dem Amazon Server leiten (dnslookup entfernen):
smarthost:
  driver = manualroute
  domains = ! +local_domains
  transport = remote_smtps
  route_data = email-smtp.us-east-1.amazonaws.com
  no_more
2. Einen neuen SMTP/SSL Transport definieren:
remote_smtps:
  driver = smtp
  protocol = smtps
  #port = 465
  hosts_require_auth = *
  hosts_require_tls = *
3. Zusätzlich einen Authenticator mit dem SES User/Passwort definieren:
client_login:
  driver = plaintext
  public_name = LOGIN
  client_send = : xxxxI6CIH2YIWSNxxxxx : Agcq00AEvA2ZDiQHNrDvTEODE3FWa1rxxxxx
Ich habe das ganze unter Amazon Linux mit Exim 4.77 aus dem AltCent Repository getestet:
wget http://centos.alt.ru/repository/centos/6/i386/exim-4.77-1.el6.i686.rpm
rpm -i exim-4.77-1.el6.i686.rpm
Es bietet sich an Exim so zu konfigurieren, dass nur auf localhost auf dem Submission Port E-Mails angenommen werden:
daemon_smtp_ports = 587
local_interfaces = 127.0.0.1
Dies stellt sicher, dass der Dienst nicht für Spam oder Sicherheitsangriffe ausgenutzt werden kann.

Java 7 - Probleme mit neuen JSSE Features

Mit Java 7 sind in den SSL/TLS Provider von Oracle einige neue Funktionen eingezogen. Darunter der schon lange erwartete Support für TLSv1.1 und TLSv1.2, aber auch die Unterstützung der TLS Extensions u.A. für die Server Name Indication(SNI). Letzteres wird dazu verwendet virtuelle Hosts auf einem SSL Port zu unterstützen: Bisher konnte ein SSL Server nämlich nicht wissen an welchen (der potentiell vielen) virtuellen Dienste hinter einer IP Adresse sich der SSL Client wenden will. Besonders ärgerlich ist dies im Fall von HTTP/s, dort ist es die Regel dass Hoster sehr viele Kunden-Domains hinter ein und der selben IP-Adresse betreiben. In HTTP/1.1 wird der gewünschte Servername in der Anfrage mitgegeben (Host: Header). So kann der HTTP Server entscheiden welche Webseiten er ausliefern soll. Im Falle des SSL Server Zertifikats (welches im SSL Handshake schon vor der HTTP Anfrage ausgetauscht wird), kann dies der Webserver aber nicht. Er muss raten welches Zertifikat er dem Client präsentieren soll, und das schlägt natürlich in der Regel fehl. Mit der Extension wird der Servername auch im Handshake mitgeschickt, und der Server kann sein Zertifikat passend auswählen. Problem bei der Geschichte ist: der Server darf auf eine solche Namensanfrage mit einem SSL Alert (Warning) reagieren. In dieser sagt er, dass er sich für den angefragten Host nicht zuständig fühlt. Das kommt bei aktuellen Webserver Installationen häufig vor, weil diese einfach nicht korrekt eingerichtet sind (und die modernen Browser die SNI unterstützen diese Warnung auch einfach ignorieren). Da das zurückgelieferte Default Zertifikat oftmals den richtigen Hostnamen (in einer der Attribute) enthält, klappt der gesicherte Handschlag im Alltag dennoch. Nicht jedoch mit Java 7 SSL Clients, JSSE macht daraus eine fatale Exception:
javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name
	at sun.security.ssl.ClientHandshaker.handshakeAlert
	at sun.security.ssl.SSLSocketImpl.recvAlert
	at sun.security.ssl.SSLSocketImpl.readRecord
	at sun.security.ssl.SSLSocketImpl.performInitialHandshake
	at sun.security.ssl.SSLSocketImpl.startHandshake
	at sun.security.ssl.SSLSocketImpl.startHandshake
	at sun.net.www.protocol.https.HttpsClient.afterConnect
	at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect
	at sun.net.www.protocol.http.HttpURLConnection.getOutputStream
	at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream
Ich habe deswegen einen Bugreport aufgemacht, jedoch wurde dieser wieder kommentarlos geschlossen. Falls Sie nun trotz Oracle's widerstreben die Notwendigkeit haben mit einem Web Server zu kommunizieren der SNI nicht richtig eingerichtet hat, so bleiben nur 2 Möglichkeiten über: a) TLS aus der Liste der unterstützten Protokolle entfernen - mit einem SSLv3 Handshake wird kein Extension Record übertragen, und entsprechend klappt auch der Handshake (solange der Server SNI nicht benötigt). b) den SSL Socket so initialisieren, dass die SSLEngine den Host nicht kennt. Dieser sogenannte Host hint wird für mehrere Dinge verwendet, kann aber auch weggelassen werden. Erreichen kann man dies, indem man den Socket statt mit s=factory.createSocket(name, port); mit "s=factory.createSocket(); s.connect(name,port);" erzeugt. Übrigens ist dies ein ziemlich unerwartetes Verhalten: SSL mit Kerberos Authentifizierung würde nur auf die erste Art und Weise funktionieren, da hierfür die Identität des Servers bekannt sein muss. Der Punkt a) ist ein schneller Fix, kommt aber in der Praxis eigentlich nicht in Frage, da man mit Java 7 ja eher daran Interesse hat TLSv1.1 oder TLSv1.2 zu verwenden um Lücken wie z.B. den BEAST-Angriff auszuschließen. Daher bleibt es nur übrig entweder den Anwendungscode zu ändern (oder wenn man diesen nicht selbst geschrieben hat, wie im Falle einer häufig verwendeten URLConnection oder beim Apache HTTPClient) oder aber mindestens eine eigene SSLSocketFactory zu implementieren, die auf die 2-stufige Erzeugung des SSLSockets aufsetzt. Update: Ich habe in den Sourcen grade eine System Property gefunden, mit der man abschalten kann, dass der ClientHandshaker die SNI Extention sendet. Dies lässt sich als Work around gut verwenden: System.setProperty("jsse.enableSNIExtension", "false"); (muss vor der Verwendung von SSL Klassen im Programm, oder auf der Command Line gesetzt werden).