flawopen.com/xss/Java
XSS Java: amankan output Spring MVC dan Thymeleaf
th:utext dan JSP tanpa escape dapat menjalankan JavaScript. Lakukan encoding sesuai konteks dan sanitasi HTML dengan allowlist (CWE-79).
Papan skor stadion memiliki kolom nama biasa dan mode tampilan kaya yang terpisah. th:text milik Thymeleaf memperlakukan nama kiriman sebagai teks literal; th:utext meminta renderer memperlakukan nilainya sebagai instruksi HTML untuk papan. Browser menerima text/html dari situs stadion dan menampilkan markup dengan otoritas situs tersebut. Pertahankan teks pengguna pada keluaran yang di-escape, lalu sanitasi sebelum memakai slot template tanpa escape jika format kaya memang diperlukan.
Konsep Kunci & Istilah
Thymeleaf th:text vs th:utextth:textmeng-escape nilainya sebagai HTML;th:utext(teks tanpa escape) menyisipkannya sebagai markup. Dalam bentuk inline Thymeleaf,[[${x}]]meng-escape dan[(${x})]tidak.th:utextyang terikat ke input pengguna adalah XSS kecuali nilainya sudah disanitasi lebih dulu.JSP ${} vs <c:out>- Di teks template JSP,
${param.q}ditulis tanpa escape.<c:out value="${param.q}"/>atau${fn:escapeXml(param.q)}meng-escape-nya. Scriptlet seperti<%= request.getParameter("q") %>tidak pernah meng-escape. Encode.forHtml / forJavaScript- OWASP Java Encoder punya satu metode per konteks output:
Encode.forHtmluntuk isi elemen,forHtmlAttributeuntuk atribut berkutip,forJavaScriptuntuk literal string JavaScript, danforUriComponentuntuk parameter URL. Meng-encode HTML untuk nilai yang ditaruh di dalam skrip tidak cukup. OWASP Java HTML Sanitizer- Untuk HTML dari pengguna, policy seperti
Sanitizers.FORMATTING.and(Sanitizers.LINKS)memegang allowlist tag dan atribut serta membuang skrip, event handler, dan URLjavascript:. Encoding akan menampilkan tag sebagai teks; sanitasi membuat tag yang aman tetap berfungsi. @ResponseBody returning text/html- Metode
@RestControlleryang mengembalikanStringdenganproduces = text/htmlditulis ke respons apa adanya: tidak ada mesin template yang berjalan, jadi tidak ada yang di-escape. Utamakan template view, atau encode setiap nilai yang digabungkan.
Pemetaan kerangka kerja
- Referensi CWE untuk kelemahan yang dijelaskan dalam panduan ini: CWE-79
- OWASP Top 10:2025 — A05:2025 Injection
Alur Serangan Langkah demi Langkah
Input datang sebagai parameter request
Spring mengikat q, user, atau bio dari query string atau body formulir ke argumen String. Korban bisa dikirimi tautan yang sudah dirancang, atau bio yang dikirim ditampilkan ke pengguna lain.
Penggabungan string menyusun respons
Controller menempelkan nilai ke HTML atau ke blok <script>. Dengan nilai uji yang tidak berbahaya <img src=x onerror=alert(1)>, string yang dikembalikan berisi tag sungguhan; dengan ';alert(1);//, blok skrip mendapat perintah baru.
Browser menguraikannya
Karena metode menyatakan text/html, browser merender body sebagai halaman. Gambar gagal dan memicu onerror, atau blok skrip menjalankan perintah yang disisipkan.
Skrip berjalan atas nama situs
Kode dijalankan dengan origin aplikasi: ia bisa membaca DOM, memanggil endpoint aplikasi dengan cookie sesi korban, dan mengubah apa yang dilihat pengguna.
Kode Sumber: Rentan vs Aman
// ProfileController.java: setiap endpoint menggabungkan data request ke HTML
@RestController
public class ProfileController {
// konteks HTML: nilai query disisipkan sebagai markup
@GetMapping(value = "/search", produces = MediaType.TEXT_HTML_VALUE)
public String search(@RequestParam String q) {
return "<h1>Results for " + q + "</h1>";
}
// konteks JavaScript: tanda kutip di user mengakhiri literal string
@GetMapping(value = "/widget", produces = MediaType.TEXT_HTML_VALUE)
public String widget(@RequestParam String user) {
return "<script>const user = '" + user + "';</script>";
}
// teks kaya dikembalikan lengkap dengan semua tag dan handler
@PostMapping(value = "/preview", produces = MediaType.TEXT_HTML_VALUE)
public String preview(@RequestParam String bio) {
return "<div class=\"bio\">" + bio + "</div>";
}
}
// ProfileController.java: setiap nilai di-encode untuk konteksnya atau disanitasi
@RestController
public class ProfileController {
private static final PolicyFactory BIO_POLICY = Sanitizers.FORMATTING.and(Sanitizers.LINKS);
// forHtml meng-encode < > & " ' untuk isi elemen
@GetMapping(value = "/search", produces = MediaType.TEXT_HTML_VALUE)
public String search(@RequestParam String q) {
return "<h1>Results for " + Encode.forHtml(q) + "</h1>";
}
// forJavaScript meng-escape tanda kutip, backslash, dan urutan </script>
@GetMapping(value = "/widget", produces = MediaType.TEXT_HTML_VALUE)
public String widget(@RequestParam String user) {
return "<script>const user = '" + Encode.forJavaScript(user) + "';</script>";
}
// sanitizer mempertahankan format dan tautan lalu membuang sisanya
@PostMapping(value = "/preview", produces = MediaType.TEXT_HTML_VALUE)
public String preview(@RequestParam String bio) {
return "<div class=\"bio\">" + BIO_POLICY.sanitize(bio) + "</div>";
}
}
Daftar Periksa Penguatan Sistem Rekayasa
- ✓Aktifkan Find Security Bugs di SpotBugs dan gagalkan build pada
XSS_SERVLET,XSS_JSP_PRINT, danXSS_REQUEST_PARAMETER_TO_SERVLET_WRITER. - ✓Cari di CI
th:utext,[(,<%=, dan${...}telanjang di JSP pada template; setiap temuan harus merender konten yang sudah disanitasi atau konstan. - ✓Encode untuk konteks yang tepat dengan OWASP Java Encoder (
forHtml,forHtmlAttribute,forJavaScript,forUriComponent) setiap kali controller menyusun markup sendiri. - ✓Lewatkan HTML dari pengguna ke policy OWASP Java HTML Sanitizer sebelum sampai ke
th:utextatau body respons. - ✓Konfigurasikan
headers().contentSecurityPolicy(...)di Spring Security denganscript-srcberbasis nonce, dan pertahankan cookie sesiHttpOnlysertaSameSite=Lax.