Skip to main content

Cross-Site-Scripting (XSS) in Java-Anwendungen mit Snyk Code verhindern

Artikel von
Snyk Code - Static Application Security Testing re-imagined for the developer

25. April 2023

0 Min. Lesezeit

Java ist eine leistungsstarke Backend-Programmiersprache, mit der sich auch HTML-Seiten für Webanwendungen schreiben lassen. Entwickler müssen sich jedoch der potenziellen Sicherheitsrisiken bewusst sein, die mit Cross-Site-Scripting-Angriffen (XSS) verbunden sind, wenn sie solche Seiten erstellen. Mit dem Aufkommen moderner Template-Frameworks ist es einfacher geworden, Sicherheitsangriffe durch geeignete Eingabevalidierungs- und Kodierungstechniken zu verhindern. Wenn Entwickler jedoch eigene HTML-Seiten ohne ein Template-Framework erstellen, steigt das Risiko, Schwachstellen einzuführen. 

Wenn Sie beispielsweise in einer Spring-MVC-Anwendung das Objekt HttpServletResponse verwenden, um Inhalte direkt in die Antwort zu schreiben, können böswillige Nutzer Code in die Seite einschleusen und so potenzielle XSS-Angriffe ermöglichen. Entwickler müssen daher geeignete Maßnahmen ergreifen, um die Sicherheit ihrer Java-Webanwendungen auf hohem Niveau zu halten und XSS-Schwachstellen beim Schreiben von HTML-Seiten zu verhindern.

Eine Lösung wie die folgende ist eine einfache Möglichkeit, eine serverseitig gerenderte Seite ohne ein aufwendiges Framework mit den üblichen spezifischen Anweisungen zu implementieren. Diese Methode hat jedoch natürlich auch einige Nachteile.

HTML-Ausgabe in Spring MVC ohne Template-Framework schreiben

Angenommen, Sie haben eine Webanwendung, die den Namen eines Produkts entgegennimmt und ihn mithilfe des Objekts HttpServletResponse auf einer Webseite anzeigt. So könnten Sie diese Funktion in einem Spring-MVC-Controller implementieren:

@GetMapping("/direct")
public void directLink (@RequestParam String param, HttpServletResponse response) throws IOException {
   Product prod = productService.getProductByName(param);

   response.setContentType("text/html");
   var writer = response.getWriter();
   writer.write(head);
   writer.write("<div class=\"panel-heading\"><h1>"+ param + "</h1></div>");

   String output = "<div class=\"panel-body\">" +
           "<ul>" +
           "<li>%s</li>" +
           "<li>%s</li>" +
           "<li>%s</li>" +
           "</ul>" +
           "</div>";

   writer.write(String.format(output, prod.getDescription(), prod.getProductType(), prod.getPrice()));
   writer.write(foot);

   response.getWriter().flush();
}

Können Sie erkennen, welche Arten von Sicherheitslücken durch den obigen Java-Code entstehen könnten?

XSS mit Snyk Code erkennen

Wenn Sie sich die obige Funktion genau ansehen, erkennen Sie vielleicht bereits mindestens eine und möglicherweise sogar zwei XSS-Schwachstellen. Beim Scan meiner Anwendung mit Snyk Code werden zwei verschiedene XSS-Probleme in dieser Methode gemeldet.

Snyk Code lässt sich auf verschiedene Arten einsetzen. Sehen wir uns drei Beispiele an. Am direktesten erhält ein Entwickler Feedback von Snyk Code, indem er ein Plugin in der IDE installiert. Für viele verschiedene IDEs stehen Plugins zur Verfügung. Im folgenden Beispiel zeige ich, wie mir das IntelliJ-Plugin dabei hilft, XSS-Probleme während der Entwicklung zu finden.

Ausgabe des IntelliJ-Plugins:

Code-Editor mit der Datei ProductController.java, in der zwei Cross-Site-Scripting-Schwachstellen in den Zeilen 93 und 103 markiert sind.

Eine weitere Möglichkeit ist, Snyk Code über die Snyk CLI auszuführen. Wenn Sie im Terminal den Befehl snyk code test ausführen, erhalten Sie eine Ausgabe wie die folgende. Diese Methode eignet sich für den Einsatz auf Ihrem lokalen Rechner oder als Teil Ihres automatischen Builds in einer CI/CD-Pipeline.

CLI-Ausgabe:

Die Terminalausgabe listet zwei Cross-Site-Scripting-Schwachstellen (XSS) mit hohem Schweregrad in ProductController.java in den Zeilen 93 und 103 auf.

Als dritte Möglichkeit möchte ich Ihnen die Weboberfläche zeigen. Für diese Ausgabe habe ich die Git-Integration von Snyk verwendet und mein GitHub-Repository über das Dashboard unter https://app.snyk.io mit der Snyk-Weboberfläche verbunden. Diese Lösung scannt den Code in meinem Repository auf Sicherheitslücken. 

Ausgabe der Weboberfläche:

Snyk Code-Bericht mit zwei Cross-Site-Scripting-Befunden (XSS), hervorgehobenem Java-Code und der Bewertung 825.

Alle drei Scan-Optionen zeigen mir zwei unterschiedliche XSS-Sicherheitsprobleme, die ich beheben muss – Snyk Code lokalisiert sie exakt in meinem Code. Sehen wir uns die Probleme genauer an und wie wir sie entschärfen können.

Reflektiertes XSS 

Reflektiertes XSS ist eine Art von XSS-Angriff, bei dem ein Nutzer bösartigen Code in eine Webanwendung einschleust, der anschließend als Teil einer Antwort an den Nutzer zurückgegeben wird. In meinem Beispiel könnte ein böswilliger Nutzer ein Skript einschleusen, wenn die Nutzereingabe vor dem Schreiben in die Antwort nicht ordnungsgemäß validiert oder bereinigt wird. Dieses Skript würde dann von anderen Nutzern ausgeführt, die die Webseite aufrufen. Diese Art von XSS-Angriff wird häufig dazu verwendet, Nutzerdaten zu stehlen, Webseiteninhalte zu verändern oder andere böswillige Aktionen auszuführen.

Der obige Code ruft den Namen des Nutzers aus dem HTTP-Anfrageparameter ab und schreibt ihn anschließend direkt in das HttpServletResponse-Objekt:

writer.write("<div class=\"panel-heading\"><h1>"+ param + "</h1></div>")

Dieser Code ist anfällig für XSS-Angriffe, da er die Nutzereingabe nicht ordnungsgemäß validiert oder bereinigt. Ein böswilliger Nutzer könnte beispielsweise HTML- oder JavaScript-Code in den Parameter „name“ einschleusen, der dann von anderen Nutzern ausgeführt würde, die die Webseite aufrufen.

Zum Beispiel könnte .../direct?param=<script>alert(document.cookie);</script> Ihre persönlichen Cookie-Daten offenlegen. Das bedeutet, dass diese Informationen auch ohne Ihr Wissen an einen anderen Server gesendet werden können.

Snyk Code hat diesen Fehler erkannt und mich auf das XSS-Problem in Zeile 93 hingewiesen.

Gespeichertes XSS

Gespeichertes XSS ist dagegen eine Art von XSS-Angriff, bei dem der bösartige Code auf dem Server gespeichert und anschließend allen Nutzern bereitgestellt wird, die die betroffene Seite aufrufen. In meinem Beispiel könnte ein böswilliger Nutzer ein Skript einschleusen, wenn die Nutzereingabe nicht ordnungsgemäß validiert oder bereinigt, sondern stattdessen in einer Datenbank gespeichert wird. Das Skript würde dann allen Nutzern bereitgestellt, die die betroffene Seite aufrufen. Diese Art von XSS-Angriff kann besonders gefährlich sein, da sie viele Nutzer betreffen und auch nach Behebung der ursprünglichen Einschleusung bestehen bleiben kann.

Der obige Code ruft ein Produkt aus dem ProductService ab und zeigt es anschließend in den Feldern als Teil der Ausgabezeichenfolge an. Dieser Code ist jedoch anfällig für gespeicherte XSS-Angriffe, da er die aus der Datenbank stammende Eingabe nicht ordnungsgemäß validiert oder bereinigt. Die Bereinigung ist besonders wichtig, wenn Sie nicht sicher sind, wer Schreibberechtigungen für die Datenbank hat. Ein böswilliger Nutzer könnte beispielsweise eine Produktbeschreibung mit HTML- oder JavaScript-Code übermitteln, die dann in der Datenbank gespeichert und allen Nutzern angezeigt würde, die die Produktansicht aufrufen. 

Snyk Code hat auf dieses potenzielle XSS-Problem in Zeile 103 hingewiesen, wo wir product.description ohne Validierung oder Bereinigung in die Ausgabezeichenfolge einfügen.

XSS-Schwachstellen mit Snyk Code entschärfen

Um XSS-Schwachstellen zu verhindern, ist es wichtig, Nutzereingaben ordnungsgemäß zu validieren und zu bereinigen, bevor sie in die Antwort geschrieben werden. Snyk Code hilft uns bereits, indem es mögliche Lösungen aufzeigt. Eine Möglichkeit besteht darin, eine Bibliothek wie Apache Commons Text zu verwenden, um die Eingabe zu kodieren und die Ausführung bösartigen Codes zu verhindern.

Code-Diff, der zeigt, wie ein Request-Pfad mit StringEscapeUtils.escapeHtml4 HTML-escaped wird, um Cross-Site-Scripting zu verhindern.

Mit der Funktion escapeHtml4() können wir sicherstellen, dass Code bei reflektiertem und gespeichertem XSS maskiert wird und beim Laden der Seite nicht ausgeführt werden kann.

Natürlich können auch andere Bibliotheken eine ähnliche Maskierung durchführen. Neben Apache Commons Text können Sie sich auch den OWASP Encoder ansehen. Wenn Sie mit Spring arbeiten, können Sie außerdem Springs HtmlUtils.htmlEscape verwenden.

Bei Verwendung von Apache Commons Text könnte der ordnungsgemäß maskierte Code so aussehen:

@GetMapping("/direct")
public void directLink (@RequestParam String param, HttpServletResponse response) throws IOException {
   Product prod = productService.getProductByName(param);

   response.setContentType("text/html");
   var writer = response.getWriter();
   writer.write(head);
   writer.write("<div class=\"panel-heading\"><h1>"+ StringEscapeUtils.escapeHtml4(param) + "</h1></div>");

   String output = "<div class=\"panel-body\">" +
           "<ul>" +
           "<li>%s</li>" +
           "<li>%s</li>" +
           "<li>%s</li>" +
           "</ul>" +
           "</div>";

   writer.write(String.format(output,
             StringEscapeUtils.escapeHtml4(prod.getDescription()),
             StringEscapeUtils.escapeHtml4(prod.getProductType()),
             StringEscapeUtils.escapeHtml4(prod.getPrice())));

   writer.write(foot);

Vorsicht bei Template-Frameworks

Template-Frameworks wie Thymeleaf können dabei helfen, XSS-Schwachstellen zu verhindern. Thymeleaf ist eine beliebte Template-Engine für Java, die HTML-Maskierung integriert unterstützt. Dadurch lassen sich XSS-Angriffe verhindern, indem Nutzereingaben kodiert werden, wenn sie in das gerenderte HTML eingefügt werden.

Allerdings hängt es stark davon ab, wie Sie das Template erstellen. So könnten Sie beispielsweise Thymeleaf verwenden, um ein Produkt ähnlich wie im vorherigen Beispiel darzustellen:

<div class="product" th:each="product : ${products}">
  <p th:text="${product.name}"></p>
  <p th:text="${product.type}"></p>
  <p th:text="${product.pric}"></p>
  <p th:utext="${product.descriptiont}"></p>
</div>

In diesem Beispiel werden die Attribute th:text maskiert, das Attribut th:utext jedoch nicht. Das Attribut th:utext rendert den Kommentartext, ohne HTML-Tags oder Sonderzeichen zu maskieren, und ist daher potenziell anfällig für XSS. Wenn Sie ein bestimmtes Framework verwenden, müssen Sie unbedingt wissen, wie sich bestimmte Elemente verhalten.

XSS erkennen, bevor Sie in die Produktion deployen

Die Verhinderung von XSS-Angriffen ist ein zentrales Anliegen für Entwickler, die an Java-Webanwendungen arbeiten. Es ist wichtig, XSS-Schwachstellen so früh wie möglich im Entwicklungsprozess zu erkennen und zu beheben. Auch wenn die Bereinigung von Nutzereingaben XSS-Angriffe wirksam entschärfen kann, reicht sie nicht immer aus.

Informieren Sie sich in Ressourcen wie dem OWASP XSS Cheat Sheet und der Snyk Learn-Lektion zu XSS über die neuesten Bedrohungen und Best Practices zur XSS-Prävention.

Darüber hinaus ist es wichtig, die richtigen Tools einzusetzen, um XSS-Fehler und andere Sicherheitsprobleme zu erkennen, bevor sie in die Produktion gelangen. Snyk Code ist ein wertvolles, kostenloses Tool, mit dem sich potenzielle Sicherheitslücken früh im Entwicklungszyklus erkennen lassen. Mit einem proaktiven Ansatz zur XSS-Prävention und den passenden Ressourcen und Tools können Entwickler dazu beitragen, die Sicherheit und Integrität ihrer Java-Webanwendungen zu gewährleisten.

Starten Sie mit Capture the Flag

Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.