HTML Tag Injection / Cross-Site Scripting (XSS): Explanation, Example, and Prevention


β What is HTML Tag Injection / Cross-Site Scripting?
Cross-Site Scripting (XSS) or HTML Tag Injection is a type of security vulnerability that allows attackers to inject malicious scripts into web pages viewed by other users. When the application fails to properly sanitize user input, attackers can execute arbitrary JavaScript in the context of another userβs session.
π₯ Example: What Can Go Wrong?
Letβs say you have a blog comment section where users can submit comments. If you directly save user input and display it back, hereβs what can happen:
<!-- User input saved as: -->
<script>alert('XSS attack!')</script>
If rendered unescaped in HTML, this will execute the JavaScript:
<p><script>alert('XSS attack!')</script></p>
Result? The browser executes it, and the user sees an alert. Worse, the script could steal cookies, redirect users, or deface the page.
π How to Prevent It (Clean Architecture Style)
β Step 1: Use a Common Utility to Sanitize Input
We'll use Apache Commons Text in Java to escape dangerous characters like <
, >
, &
, "
, '
, etc.
import org.apache.commons.text.StringEscapeUtils;
public class HtmlSanitizer {
// Escape HTML special characters
public static String sanitize(String input) {
return StringEscapeUtils.escapeHtml4(input);
}
// Optional: Decode for rendering formatted content
public static String unsanitize(String input) {
return StringEscapeUtils.unescapeHtml4(input);
}
}
Maven dependency:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.10.0</version>
</dependency>
β Step 2: Sanitize Before Saving to Database
Apply the sanitize()
method on every user input that will be stored:
@PostMapping("/comment")
public ResponseEntity<?> addComment(@RequestBody CommentRequest request) {
String safeContent = HtmlSanitizer.sanitize(request.getContent());
Comment comment = new Comment();
comment.setContent(safeContent);
commentRepository.save(comment);
return ResponseEntity.ok("Saved");
}
β Step 3: Display Safely in Frontend
Use a templating engine that escapes HTML by default (e.g., Thymeleaf, JSP):
<!-- Safe display -->
<p th:text="${comment.content}"></p>
If you really need to display formatted HTML (like markdown), decode it carefully:
<!-- Only do this if the data is fully trusted -->
<p th:utext="${T(HtmlSanitizer).unsanitize(comment.content)}"></p>
π Summary: Do It Right
Layer | Purpose | What to do |
Controller/Input | Sanitize dangerous characters | HtmlSanitizer.sanitize() |
DB layer | Store encoded safe content | Already sanitized from input |
UI layer | Escape by default / decode if needed | th:text or unsanitize() |
π Real-World Tips
Never trust user input, even from authenticated users.
Always sanitize before storing in DB.
Use frameworks that escape content automatically.
For rich text or markdown, whitelist allowed tags.
β Conclusion
HTML Tag Injection (XSS) is dangerous but easily preventable. Treat all inputs as untrusted, sanitize smartly, and render safely. Using a common utility ensures consistency across your entire app.
Stay secure and write safe code! π¨βπ»π§
Subscribe to my newsletter
Read articles from Quoc Dang directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
