Schwerpunktthema Sicherheit von der Stange Security-Tools und -Bibliotheken für (fast) jede Webanwendung Dominik Schadow Für die Entwicklung von Java-Webanwendungen steht eine wahre Flut von nützlichen kleinen Bibliotheken und umfangreichen großen Frameworks zur Verfügung. Im Bereich der sicheren Entwicklung ist das – wenn auch in kleinerem Maßstab – nicht anders. Gleichzeitig erleichtern verschiedene frei verfügbare Tools die sichere Entwicklung der Webanwendung und ermöglichen das Testen der umgesetzten Security-Features. anwendung interagiert oder kommuniziert. Und zwar sowohl die im Rahmen des Projekts neu entwickelten als auch die bereits vorhandenen Systeme, unabhängig davon, ob diese von den eigenen Entwicklern oder von einer dritten Partei entwickelt wurden. Neben den üblichen Verdächtigen – Dateneinund -ausgabe durch einen Benutzer im Browser, Speichern der Daten in einer Datenbank – tauchen dabei häufig noch weitere Systeme auf: Identity-Management-Systeme, weitere Datenbanken, Webservices und viele andere mehr. Hier gilt es, allen Datenflüssen zu folgen und in das Threat Model aufzunehmen, also etwa dem Datenfluss vom Benutzer (Eingabe im Browser) bis hin zur Speicherung in der Datenbank. Wichtig sind die in den Datenflüssen passierten Trust Boundaries. Dabei handelt es sich um die Stellen, an denen aus gefährlichen Daten (beispielsweise Benutzereingaben) vertrauenswürdige Daten werden müssen. Das ist etwa notwendig, wenn die Eingabedaten das Backend erreichen und in der Datenbank gespeichert werden sollen. Oder wenn die Daten einer Internetanwendung das Intranet erreichen und damit in eine neue Vertrauenszone gelangen. An diesen Stellen muss im Code später zumindest eine Validierung der Eingabedaten durchgeführt werden. Neben den weithin bekannten und nahezu allumfassenden großen Frameworks Spring Security [SprintSecurity] und Apache Shiro [Shiro] existieren noch zahlreiche häufig sehr spezialisierte Bibliotheken. Diese bieten passende Ergänzungen zu dem einen großen Security-Framework einer Webanwendung an und helfen bei der Lösung spezieller Anforderungen. Für die sichere Entwicklung einer Webanwendung sind darüber hinaus Tools notwendig, die Entwickler in allen Phasen des Projekts sinnvoll unterstützen. Mit dem Threat Modeling und der Prüfung von Abhängigkeiten sind das Tools, die bereits vor beziehungsweise noch nach der Implementierungsphase zum Einsatz kommen. Im Folgenden vorgeschlagen werden HMicrosoft Threat Modeling Tool 2014 [ThMT], HOWASP Java Encoder [JEP], HKeyczar [Keyczar], HOWASP Zed Attack Proxy Project [ZAP] Abb. 1: Threat Model einer Webanwendung H und OWASP Dependency Check [DeCh]. E Threat Modeling Was sich bei der Anwendungsarchitektur mit UML-Diagrammen völlig zurecht längst durchgesetzt hat, steckt bei der Anwendungssicherheit noch in den Kinderschuhen: die Modellierung. Genauso, wie ein UML-Diagramm das gemeinsame Verständnis im Entwicklungsteam fördert und zum Beispiel alle Komponenten oder Packages einer Webanwendung visualisiert, zeigt ein Threat Model alle beteiligten Systeme und die Datenflüsse zwischen ihnen aus dem Blickwinkel der Sicherheit. Ein solches Diagramm (s. Abb. 1) wird idealerweise zeitgleich mit der UML-Modellierung der Architektur erstellt, in jedem Fall aber vor Beginn der Implementierung. Im Threat Model enthalten sind alle Systeme, mit denen die zu entwickelnde Web8 Abb. 2: Microsoft Threat Modeling Tool 2014 JavaSPEKTRUM 6/2015 Schwerpunktthema Ein für das Threat Modeling gerade auch für Java-Anwendungen sehr gut geeignetes Tool ist das Microsoft Threat Modeling Tool 2014 (s. Abb. 2). Mit diesem kostenlosen Tool können nicht nur Threat Models erstellt werden. Zusätzlich listet die Anwendung anhand der eingefügten Entity auch typische Bedrohungen auf, die bei der Entwicklung entsprechend berücksichtigt werden müssen. Bei einer Datenbank-Entity ist eine typische Bedrohung etwa die allseits bekannte SQLInjection. Auch wenn die im Tool aufgeführten Bedrohungen nicht immer exakt zur eigenen Webanwendung passen und verständlicherweise nicht auf die Java-Besonderheiten eingehen, erhält man dennoch einige hilfreiche Vorschläge auf mögliche Gefahren. Je nach Komplexität der Anwendung werden ein oder mehrere dieser Threat Models erstellt (z. B. ein Übersichtsdiagramm und mehrere detaillierte Einzeldiagramme). Mindestens ein solches Diagramm ist zwingende Voraussetzung, um eine Webanwendung von innen heraus sicher zu entwickeln. Nur mit einem genauen Überblick über die Datenflüsse, die beteiligten Systeme und den Trust Boundaries ist es möglich, sichere Anwendungen zu entwickeln. Eine umfangreiche Einführung in das Thema Threat Modeling findet sich in [Sho14]. Output Escaping Mit dem nun verfügbaren Threat Model besitzen alle Entwickler ein genaues Verständnis der Webanwendung. Jetzt gilt es – neben der Validierung aller eingehenden Daten –, die zur Anzeige im Webbrowser bestimmten ausgehenden Daten zu „escapen“. Dieses Output Escaping (auch Output Encoding) ist notwendig, um die Ausführung von eingeschleustem Code zu verhindern. Gibt ein Angreifer beispielsweise <script>alert(document.cookie)</script> ein und wird dabei nicht von der Eingabevalidierung aufgehalten, führt der Browser nach Verarbeitung der Response diesen Code mit dem Rest der Webanwendung aus. Auch wenn das in Abbildung 3 gezeigte Popup-Fenster in dieser Form einem Angreifer nichts nützt, zeigt es ihm doch, dass die Webanwendung anfällig für Cross-Site-Scripting (XSS) ist und zusätzlich zur Ausführung des Codes die Session ID des angemeldeten Benutzers verrät. XSS hat meist die Erbeutung der in Abbildung 3 sichtbaren Session ID eines Benutzers zum Ziel. Damit kann ein Angreifer gegenüber der Webanwendung vorgeben, dieser Benutzer zu sein. EbenAbb. 3: Mit XSS ausgelöstes JavaScript-Popup so kann XSS zum simplen Verunstalten einer Webseite (website defacement) eingesetzt werden. Häufig dient XSS aber als Türöffner für ausgefeiltere Angriffe und macht diese erst möglich. Eine Webanwendung muss daher zwingend vor XSS-Angriffen geschützt sein. Das zugrunde liegende Problem von XSS ist, dass der Browser sämtlichen Code ausführt. Das vom Entwickler eigentlich erwartete und gewünschte Verhalten ist allerdings die Anzeige aller Benutzereingaben als normalen Text. Mittels Output Escaping lässt sich nun genau dieses Verhalten erzwingen. Damit www.javaspektrum.de stellt das Output Escaping die Gegenmaßnahme Nummer eins gegen XSS dar. Komplizierter wird das Output Escaping durch die notwendige Unterscheidung der Kontexte, zum Beispiel HTML, CSS, URI oder JavaScript. Der HTML-Kontext stellt dabei den verbreitetsten Kontext dar. Dabei werden die vom Benutzer (oder Angreifer) eingegebenen Daten im HTML-Bereich der Webseite eingefügt. Beim Output Escaping für diesen Kontext ist dazu unter anderen die bekannte Umwandlung von < und > in &lt; und &gt; notwendig. Damit zeigt der Browser selbst Code nur als Text an und führt diesen nicht aus. Diese Umwandlung mag einfach klingen, ist es in Wirklichkeit aber nicht. Durch Verwendung von Encodings kann ein Angreifer etwa eingegebenen Code selbst maskieren, oder etwa mit JavaScript-Substring-Befehlen aus einem sinnlosen Text einen gefährlichen Angriffscode zusammensetzen. Der Einsatz einer Output-Escaping-Bibliothek mit Unterstützung verschiedener Kontexte ist daher zwingend notwendig. Herausragend einfach in der Verwendung und gleichzeitig sehr sicher ist der OWASP Java Encoder. Diese kleine Bibliothek besitzt keine weiteren Abhängigkeiten und kann ohne Probleme in die eigene Webanwendung integriert werden. Listing 1 zeigt zwei Escapings für die Kontexte HTML und HTML-Attribut und die daraus resultierende sichere (da escapte) Ausgabe. try (PrintWriter out = response.getWriter()) { // ... out.println("<body>"); out.println("<p title='Hello " + Encode.forHtmlAttribute(name) + "'>Hello "); Encode.forHtml(out, name); out.println("</p>"); // ... } catch (IOException ex) { } Listing 1: Escaping für HTML und HTML-Attribute Der Entwickler hat somit nur die Aufgabe, den richtigen Kontext zu bestimmen und sämtliche Benutzereingaben vor der Rückgabe an den Browser sicher zu escapen. Wie in Listing 1 gezeigt, bedeutet das bei der Mehrfachverwendung einer Benutzereingabe in verschiedenen Kontexten, dass diese für den jeweiligen Kontext erneut escaped werden muss. Die Verwendung eines falschen Kontexts kann dazu führen, dass zumindest Teile der Eingabedaten nicht korrekt escaped und vom Browser eventuell dennoch ausgeführt werden. Kryptografie Kryptografie ist ein komplexes Thema und bei Verwendung von plain Java in der Umsetzung alles andere als einfach. Mit Keyczar steht deswegen eine Bibliothek zur Verfügung, mit der die Anwendung kryptografischer Operationen – Verschlüsseln, Entschlüsseln, Signieren und Verifizieren – deutlich einfacher wird. Mit Keyczar nimmt man dabei bewusst eine wichtige Einschränkung in Kauf: mit aktuell AES, HMAC-SHA1, DSA und RSA stehen vergleichsweise wenige Algorithmen zur Verfügung. Gleichzeitig sind für diese aber sichere Defaultwerte hinterlegt, sodass außer der Entscheidung für einen Algorithmus nichts weiter konfiguriert werden muss (diese Defaultwerte lassen sich bei Bedarf überschreiben). Wo bei Java üblicherweise das Java KeyTool und der Java KeyStore verwendet werden, setzt Keyczar mit dem KeyczarTool und den damit generierten Key Sets ebenfalls auf eigene Konzepte. So werden Schlüssel (Keys) und Schlüsselspeicher (Key 9 Schwerpunktthema Sets) im JSON-Format gespeichert und haben immer nur eine einzige kryptografische Aufgabe: Verschlüsseln (und logischerweise Entschlüsseln) oder Signieren (und logischerweise Verifizieren), und sind dabei entweder symmetrisch oder asymmetrisch. Ein Key Set hat so eine exakt bestimmte Aufgabe und speichert nur Schlüssel dieses einen Typs. Crypter crypter = new Crypter("key-sets/encrypt/rsa"); String ciphertext = crypter.encrypt(initialText); String plaintext = crypter.decrypt(ciphertext); Listing 2: Asymmetrisches Ver- und Entschlüsseln mit Keyczar Wie in Listing 2 zu sehen, ist auch das Ver- und Entschlüsseln im Code mit Keyczar sehr einfach. Soll aus dieser asymmetrischen Verschlüsselung mit RSA eine symmetrische mit AES werden, genügt es, den Key-Set-Pfad im Crypter-Konstruktor entsprechend auf das passende Key Set anzupassen. Der Aufruf der Ver- und Entschlüsselungsmethoden im restlichen Code bleibt identisch. Für digitale Signaturen existieren entsprechend die Klassen Signer und Verifier. Gleichzeitig vereinfacht Keyczar die Schlüsselverwaltung. Wie Passwörter sollten auch Schlüssel regelmäßig ausgetauscht werden. Eine Anforderung ist dabei häufig, dass die bisherigen Schlüssel für die Entschlüsselung alter Nachrichten weiterhin passiv vorgehalten werden müssen. Für aktive Operationen wie dem Verschlüsseln oder Signieren neuer Nachrichten sollen allerdings nur noch die neuen Schlüssel verwendet werden. Hierzu unterstützt Keyczar die Schlüsselversionierung. In jedem Key Set können beliebig viele Schlüsselversionen vorliegen. Solange sie aktiv sind, können sie etwa für das Entschlüsseln herangezogen werden. Aktive Operationen werden immer nur mit dem als Primary gekennzeichneten Schlüssel durchgeführt. Testen Eine typische Webanwendung ist zu diesem Zeitpunkt umfassend gesichert. Die Eingabevalidierung verhindert das Übertragen potenziell gefährlicher Daten, das Output Escaping macht dennoch eingefügten Code unschädlich. Das Problem ist nun, dass die Entwickler diese Sicherheitsmaßnahmen nicht mehr alle testen können. Bereits bei der Eingabe etwa eines script-Tags lehnt der Browser die ungültigen Daten ab. Getestet – vor allem in Bezug auf die Sicherheit – ist damit außer der leicht umgehbaren Frontend-Validierung allerdings noch überhaupt nichts. Damit das Backend nun dennoch mit ungültigen Daten in Berührung kommt, ist die Verwendung eines Intercepting Proxy notwendig. Dieses Tool hängt sich als Proxy zwischen den eigenen Browser und die eigene(!) Webanwendung und ermöglicht das Abfangen und Manipulieren aller Requests und Responses zwischen diesen beiden Endpunkten. Da solche Tools durchaus auch für Angriffe verwendet werden können, ist ihr legaler Einsatz auf die eigene Webanwendung (idealerweise auf dem eigenen Rechner) oder auf genehmigte Webanwendungen begrenzt. Ein auch für Entwickler gut geeigneter Intercepting Proxy ist OWASP ZAP. In den Browsereinstellungen wird dieses Tool zunächst als Proxy konfiguriert. Anschließend können in ZAP Requests und Responses nach Belieben abgefangen und manipuliert werden. Dieses Abfangen funktioniert per Breakpoint, analog zu den Debugging-Breakpoints in einer Entwicklungsumgebung. So lassen sich aus gültigen und im Browser validierten Daten ungültige und gefährliche Daten machen. Abbildung 4 zeigt OWASP ZAP beim Anfangen eines Requests. Die Security-Features des Backends können somit direkt getestet werden. Anschließend kann ZAP auch die Response abfangen und ermöglicht die Manipulation dieser, bevor die Daten im Browser angezeigt werden. Hiermit lässt sich etwa testen, wie ein Browser auf vermeintlich escapte Daten reagiert. Neben diesen Intercepting-Fähigkeiten bietet ZAP noch sehr viel mehr Features. Unter anderen lassen sich alle erreichbaren Seiten einer Webanwendung auflisten und deren Requests und Responses im Detail untersuchen. Nach dieser notwendigen Voruntersuchung kann der nächste Schritt darin bestehen, die in Requests enthaltenen Parameterwerte mit bekannten Angriffsmustern auszutauschen und die Webanwendung beispielsweise per Cross-Site-Scripting der SQL-Injection anzugreifen. Aktuell bleiben Abb. 4: Request-Interception mit OWASP ZAP 10 Ein besonders bei Java-Anwendungen verbreitetes Problem ist das Veralten der in der Webanwendung eingesetzten Bibliotheken. Webanwendungen beinhalten häufig 15, 20 oder mehr fremde Bibliotheken und lagern dabei mitunter auch sicherheitskritische Aufgaben an diese aus. Umso wichtiger, diese Bibliotheken aktuell zu halten und bei bekannt gewordenen Sicherheitsproblemen schnell zu reagieren. Und zwar unabhängig davon, ob es sich um eine sicherheitsrelevante Bibliothek oder nur um eine Utility-Bibliothek handelt. Für diese Aufgabe hervorragend geeignet ist OWASP Dependency Check. Dieses Tool scannt alle in einer Webanwendung enthaltenen Bibliotheken und vergleicht die identifizierten Bibliotheken mit in JavaSPEKTRUM 6/2015 Schwerpunktthema Fazit Gerade bei der Implementierung von sicherheitskritischen Features ist es wichtig, das Rad nicht selbst neu zu erfinden, sondern auf bewährte Security-Frameworks und -Bibliotheken zu setzen. Ein umfassendes Threat Modeling vor Beginn der Implementierung stellt sicher, dass diese Bibliotheken an den richtigen Stellen eingesetzt werden. Anschließende Tests mit einem Intercepting Proxy helfen, umgesetzte Sicherheits-Features zumindest teilweise zu umgehen, und ermöglichen es so, auch das geschützte System mit gefährlichen Eingaben anzugreifen. Werden dann sämtliche eingesetzten Bibliotheken stets aktuell gehalten, ist schon viel für die Sicherheit einer typischen Webanwendung gewonnen. Alle im Artikel verwendeten Code-Beispiele sind im GitHubRepository des Autors (vgl. [GitHub]) verfügbar. Literatur und Links Abb. 5: Dependency-Check-Report [DeCh] OWASP Dependency Check, https://www.owasp.org/index.php/OWASP_Dependency_Check [GitHub] JavaSecurity Repository, der National Vulnerability Database gelisteten bekannten Verwundbarkeiten. Dazu wird Dependency Check entweder per Maven-Konfiguration ins Projekt integriert oder per manuellem Aufruf über die Kommandozeile gestartet: https://github.com/dschadow/JavaSecurity [JEP] OWASP (Open Web Application Security Project) Java Encoder Project, https://www.owasp.org/index.php/OWASP_Java_Encoder_Project [Keyczar] dependency-check.sh --app JavaSecurity --scan target/dependency Mit diesem Aufruf wird ein HTML-Report mit identifizierten Bibliotheken generiert (s. Abb. 5). Diese Liste ist nicht frei von false positives. Nicht jeder Eintrag bedeutet daher gleich eine verwundbare Bibliothek in der eigenen Webanwendung. Zweifelsfreie false positives lassen sich per XML-Konfiguration in zukünftigen Scans ignorieren. Bei korrekt erkannten Verwundbarkeiten ist der Entwickler gefordert, die verwundbare Bibliothek zeitnah gegen eine neue Version auszutauschen und die Webanwendung mit der neuen Version zu testen. Als nächsten Schritt empfiehlt sich die Integration des Dependency Checks in den Jenkins Build. So kann automatisch nach jedem erfolgreichen Build ein Scan aller Abhängigkeiten durchgeführt werden. Neben dem HTML-Report wird für Jenkins zusätzlich ein Graph generiert, mit dem alle Entwickler auf einen Blick die Anzahl der verwundbaren Bibliotheken erkennen können. Mittels Grenzwerten lässt sich konfigurieren, ab wann ein Build instabil wird oder auch ganz fehlschlägt. www.javaspektrum.de oder https://github.com/google/keyczar [Shiro] http://shiro.apache.org [Sho14] A. Shostack, Threat Modeling, Wiley, 2014 [SpringSecurity] http://projects.spring.io/spring-security [ThMT] Microsoft SDL (Security Development Lifecycle) Threat Modeling Tool, 2014, http://www.keyczar.org http://www.microsoft.com/en-us/sdl/adopt/threatmodeling.aspx [ZAP] OWASP Zed Attack Proxy Project, https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project Dominik Schadow arbeitet als Senior Consultant beim IT-Beratungsunternehmen bridgingIT. Er ist spezialisiert auf die Architektur und Entwicklung von Java-Enterprise-Applikationen, Enterprise Application Integration (EAI) und die sichere Softwareentwicklung mit Java. E-Mail: [email protected] 11