Code-1
Vulnerable to Xss. Code is taken from PentesterLabs Java code review 05.
Code
Whole code:
Structure
vulnerable/
├── mvnw.cmd
├── pom.xml
├── .gitignore
├── .mvn/
│ └── wrapper/
│ ├── maven-wrapper.properties
│ └── maven-wrapper.jar
├── mvnw
├── src/
│ ├── test/
│ │ └── java/
│ │ └── com/
│ │ └── pentesterlab/
│ │ └── vulnerable/
│ │ └── VulnerableApplicationTests.java
│ ├── main/
│ │ ├── resources/
│ │ │ ├── static/
│ │ │ │ ├── css/
│ │ │ │ │ ├── bootstrap.css
│ │ │ │ │ └── pentesterlab.css
│ │ │ ├── templates/
│ │ │ │ ├── index.html
│ │ │ │ └── fragments.html
│ │ │ └── application.properties
│ │ └── java/
│ │ └── com/
│ │ └── pentesterlab/
│ │ └── vulnerable/
│ │ └── controller/
│ │ └── MainController.java
│ │ └── VulnerableApplication.java
VulnerableApplication.java
package com.pentesterlab.vulnerle;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class VulnerableApplication {
public static void main(String[] args) {
SpringApplication.run(VulnerableApplication.class, args);
}
}
MainController.java
package com.pentesterlab.vulnerable.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class MainController {
@GetMapping("/")
public String showHomePage(HttpServletRequest request, Model model) {
String name = request.getParameter("name");
if (name != null) {
model.addAttribute("name", name);
}
return "index";
}
}
Index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{fragments :: header}">
</head>
<body>
<div class="container">
<div class="header" th:replace="~{fragments :: menu}">
</div>
<div class="row">
<div class="col-lg-12">
<h1>Welcome</h1>
<p th:utext="'Hello, ' + ${name} + '!'" />
<p>You can start by personalising the page with your name by using <a th:href="@{/?name=hacker}">this link</a></p>
</div>
<div class="col-lg-12" th:replace="~{fragments :: footer}">
</div>
</div>
</div>
</body>
</html>
First we need to learn this:
SpringApplication.run(...)
does:
Builds an
ApplicationContext
(the Spring IoC container).Scans for components (
@Controller
,@Service
,@Component
,@Repository
,@Configuration
, etc.).Instantiates those bean classes (calling their constructors) and performs dependency injection.
Calls lifecycle callbacks (e.g.,
@PostConstruct
,InitializingBean.afterPropertiesSet()
), and runs anyCommandLineRunner
beans after the context is ready.
So the constructors for beans are invoked during context startup, not because main()
called them directly.
Spring will instantiate
MainController
at startup (constructor called then).showHomePage(...)
will only be invoked when a client requestsGET /
— e.g., the user openshttp://host/?name=alice
.The controller’s constructor is not what invokes
showHomePage
. So handlers don't run automatically.
Vulnerability
The controller reads user input directly from the request:
String name = request.getParameter("name");
if (name != null) {
model.addAttribute("name", name);
}
The Thymeleaf template uses
th:utext
:
<p th:utext="'Hello, ' + ${name} + '!'" />
th:utext
inserts unescaped HTML into the page. Ifname
contains HTML or script, it will be interpreted by the browser.
A user can craft a URL such as:
http://app/?name=<script>alert(1)</script>
Fix
Template Side fix:
<!-- vulnerable -->
-<p th:utext="'Hello, ' + ${name} + '!'" />
+<!-- safe -->
+<p th:text="'Hello, ' + ${name} + '!'" />
th:text
escapes HTML entities so "<script>"
becomes <script>
, so it is shown as text, not executed.
2) Server-side defense (defense-in-depth)
If you want extra safety or are rendering data in other contexts, escape/sanitize on the server:
Escape HTML (Spring utility)
import org.springframework.web.util.HtmlUtils;
String safe = HtmlUtils.htmlEscape(name);
model.addAttribute("name", safe);
or sanitize to allow only safe HTML (if you specifically intend to allow limited HTML) using JSoup:
import org.jsoup.Jsoup;
import org.jsoup.safety.Safelist;
String safe = Jsoup.clean(name, Safelist.basic()); // allow only a small, safe subset
model.addAttribute("name", safe);
Last updated