Sichere Java-Webanwendungen, Teil 1: Cross-Site Scripting

Seite 2: Schutzmechanismen

Inhaltsverzeichnis

Grundsätzlich sind mehrere Sicherungsmaßnahmen gemeinsam eingesetzt immer besser und ergeben einen vollständigeren Schutz als eine einzige. Idealerweise kombiniert man zur umfassenden Absicherung vor XSS-Angriffen daher alle im Folgenden beschriebenen Gegenmaßnahmen.

Den einfachsten Schutz vor dem Auslesen der Session-ID (JSESSIONID in Java-Webanwendungen) stellt der Schutz des Session-Cookies dar. Durch eine simple Anpassung der web.xml lässt sich damit zwar XSS nicht grundsätzlich verhindern, aber immerhin der Diebstahl der Session-Informationen aus dem Session-Cookie:

<?xml version="1.0" encoding="UTF-8"?>
<web-app ... version="3.1">
<!-- ... -->
<session-config>
<session-timeout>30</session-timeout>
<cookie-config>
<http-only>true</http-only>
<secure>true</secure>
</cookie-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>
</web-app>

Die Angabe von session-timeout sorgt dafür, dass eine vom Benutzer nicht explizit beendete Session ewig gültig ist. Stattdessen läuft eine inaktive Session nach der festgelegten Minutenanzahl automatisch ab. Mit dem Wert COOKIE für tracking-mode verbannt der Entwickler die JSESSIONID aus der URL in das Session-Cookie und sorgt so für geringfügig mehr Schutz vor der Übermittlung an eine fremde URL. Schließlich sorgt http-only dafür, dass Skripte nicht mehr an das Session-Cookie herankommen, und secure verhindert dessen Übertragung über eine nicht per SSL/TLS gesicherte Verbindung. Eine solchermaßen geschützte web.xml sollte in jedem neuen Webprojekt immer als Erstes angelegt werden.

Die vergleichsweise einfach einzusetzende Eingabevalidierung ist eine häufig unterschätzte (Teil-)Schutzmaßnahme gegen zahlreiche Angriffe auf Webanwendungen. Gleichwohl geht damit bei vielen Eingabemöglichkeiten ein gewisser Konfigurationsaufwand einher. Neben XSS schützt eine konsequente Validierung allerdings vor SQL-Injection-Angriffen. Wobei hier wie dort die Eingabevalidierung nicht als einzige Schutzmaßnahme eingesetzt werden darf, sondern in jedem Fall von weiteren Maßnahmen zu begleiten ist.

Neben den üblichen Verdächtigen wie Formularen sind sämtliche Eingabekanäle zu beachten. Darunter fallen auch URL-Parameter, Cookies, Webservices oder von fremden Schnittstellen konsumierte Daten.

Wichtig ist, dass die Validierung in jedem Fall im Backend stattfindet, eine frontendseitige Validierung lässt sich zu leicht umgehen und ist aus Sicherheitsperspektive daher optional. Die Bean Validation (z. B. in der Java-Referenzimplementierung Hibernate Validator) ist hierfür gut geeignet, ermöglicht doch die Annotation @Pattern() die Angabe eines regulären Ausdrucks:

public class User {
@Pattern(regexp="^[a-z0-9]+")
private String username;
// ...
}

Nur solchermaßen per regulärem Ausdruck überprüfte Daten bieten einen guten Schutz vor Schadcode. Eine simple Annotation zur Überprüfung auf Textinhalt oder -länge bietet keinen Sicherheitsgewinn: Der Schadcode ist schließlich nichts weiter als gewöhnlicher – allerdings ausführbarer – Text. Selbst längenbegrenzte Eingabefelder lassen sich für XSS-Angriffe nutzen.

Auch wenn die Validierung sämtlicher Eingabedaten eine Webanwendung deutlich sicherer macht, ersetzt sie keinesfalls das Output Escaping. Vielmehr ist es gerade andersherum: Ein vollständiges Output Escaping ersetzt theoretisch die Eingabevalidierung. Die gefährlichen Daten gelangen damit zwar in die Webanwendung, lassen sich dort aber nicht ausführen:

Trotzdem sollten Entwickler nicht auf eine Validierung verzichten. Zum einen sollen fehlerhafte oder gefährliche Daten natürlich überhaupt nicht in die Webanwendung gelangen. Zum anderen können die Daten ja auch in anderen Webanwendungen verwendet werden, in denen vielleicht kein Output Escaping stattfindet.

Idealerweise verwenden Entwickler für das hier aus Security-Perspektive durchgeführte Output Escaping eine auf Security Escapings spezialisierte Bibliothek wie OWASP Java Encoder oder die Coverity Security Library. Mit der ersten lassen sich Escapings für Ausgaben zum Beispiel in einer JSP-Datei direkt durchführen:

<%@ page import="org.owasp.encoder.Encode" %>
<html>
<body>
<strong>Hello</strong>
<%= Encode.forHtml(request.getParameter("name")) %>
</body>
</html>

Im Listing wird an der aufgerufenen Methode forHtml() unmittelbar deutlich, dass der Kontext zu beachten ist, in den die Benutzereingaben eingefügt werden. Neben dem verbreitetsten Kontext HTML sind Attributwerte, CSS, JavaScript, URL oder andere möglich. Kann ein Benutzer beispielsweise Einfluss auf die Hintergrundfarbe im Stylesheet der Seite nehmen, ist diese Eingabe für CSS zu "maskieren":

<%= Encode.forCssString(request.getParameter("color")) %>

Ein falsch gewählter Kontext kann zu einem falsch oder gar nicht ausgeführten Escaping führen, gefährliche Eingaben werden dann eventuell nicht korrekt entschärft.