flawopen.com/Cross-Site Scripting/Java

Cross-Site Scripting in Java

High CWE-79 Draft — pending review
Language: English Português (Brasil) Español Français Deutsch Русский 简体中文 日本語 हिन्दी 한국어 Bahasa Indonesia
ELI5

Imagine a guestbook where visitors write a public note. Stored XSS is like someone writing a note that isn't just text — it's a hidden trick that makes the guestbook page itself start doing things, like stealing the next visitor's login session, the moment anyone opens the page to read it.

Key terms on this page
output encoding
Converting characters that have special meaning in HTML (like < and >) into harmless equivalents before inserting untrusted text into a page, so the browser displays it as text instead of running it as markup or script.
DOM
The in-memory tree structure a browser builds from a page's HTML — where an element's innerHTML is set determines whether inserted content is rendered as inert text or executable markup.

What's happening

Cross-site scripting happens when untrusted input reaches the rendered page without proper output encoding. In Java web apps this shows up in JSP scriptlets that print a value raw, and in template engines like Thymeleaf when their explicit "unescaped" output mode is used for untrusted data.

Real-world impact

In 2005, an 19-year-old user exploited a stored XSS flaw on MySpace to create the "Samy" worm — a script that added itself to every profile that viewed it, infecting over a million profiles within about 20 hours and forcing MySpace offline to contain it. It remains one of the most-cited demonstrations of how quickly stored XSS can self-propagate.

Source: widely documented in security industry retrospectives — see References below.

Vulnerable vs. fixed

VULNERABLE
<!-- JSP scriptlet, no escaping -->
<div>
  Welcome, <%= request.getParameter("name") %>
</div>
FIXED
<!-- JSTL c:out escapes by default -->
<div>
  Welcome, <c:out value="${param.name}"/>
</div>

Why the fix works

<c:out> HTML-encodes its value by default before writing it to the response — special characters become their entity equivalents, so a <script> tag in the parameter renders as visible text. A raw JSP scriptlet expression (<%= %>) writes exactly what it's given, with no encoding at all.

Java-specific gotchas

Thymeleaf's th:utext means exactly what it says: unescaped text

th:text escapes by default; th:utext is the explicit, named escape hatch for when you genuinely need to render real HTML — using it for untrusted data reopens the exact same hole as a raw JSP expression.

Raw JSP scriptlet expressions never escape

<%= someValue %> writes its value directly to the response with zero encoding — it's the JSP equivalent of building HTML with string concatenation, and it's still common in legacy codebases.

Spring's @ResponseBody returning raw HTML strings bypasses templating entirely

A controller method that builds and returns an HTML string directly (rather than rendering a template) gets none of the template engine's default escaping — the responsibility shifts entirely to manual encoding.

Common misconceptions

"Thymeleaf/JSTL auto-escapes everything, so I'm safe by default"

True for th:text and c:out — false for th:utext or a raw scriptlet expression.

"I only need to worry about this in comment sections"

Any reflected value counts — a search query echoed on a results page, a URL parameter shown in an error message, a filename displayed after upload.

How to check if you're affected

grep -rn "th:utext" --include="*.html" . grep -rn "<%=" --include="*.jsp" .
SpotBugs with the FindSecBugs plugin flags unescaped output patterns (XSS_REQUEST_PARAMETER_TO_SEND_ERROR and related rules) automatically in CI.

Prevention checklist

FAQ

Is this the same bug as SQL injection?

Same underlying shape — untrusted data mixed into a command's structure without encoding — but the target and damage differ: SQL injection targets the database, XSS targets other users' browsers.

Does Spring Security help with this?

Spring Security can add security headers like Content-Security-Policy as a second layer of defense, but it doesn't perform output encoding for you — that's still the template engine's job.

References

View in: Python JavaScript Go Java PHP C# Ruby C/C++ Rust Kotlin Swift Solidity (N/A)