flawopen.com/xss/Java
Java XSS: Spring MVC और Thymeleaf आउटपुट सुरक्षित करें
th:utext और बिना escape JSP JavaScript चला सकते हैं। संदर्भ के अनुसार encode करें और HTML को allow-list से साफ़ करें (CWE-79)।
स्टेडियम के scoreboard में सामान्य नाम का field और rich display का अलग mode होता है। Thymeleaf का th:text दिए गए नाम को अक्षरशः text मानता है; th:utext renderer से उस value को बोर्ड के HTML निर्देश की तरह लेने को कहता है। Browser स्टेडियम साइट का text/html पाता है और उस markup को साइट के अधिकार से render करता है। User text को escaped output में रखें; rich formatting की आवश्यकता हो तो unescaped template slot में देने से पहले sanitize करें।
इस पेज के मुख्य शब्द
Thymeleaf th:text vs th:utextth:textअपने मान को HTML-एस्केप करता है;th:utext(बिना एस्केप का टेक्स्ट) उसे मार्कअप की तरह डालता है। Thymeleaf के inline रूप में[[${x}]]एस्केप करता है और[(${x})]नहीं। यूज़र इनपुट से जुड़ाth:utextXSS है, जब तक मान पहले सैनिटाइज़ न हुआ हो।JSP ${} vs <c:out>- JSP टेम्पलेट टेक्स्ट में
${param.q}बिना एस्केप के लिखा जाता है।<c:out value="${param.q}"/>या${fn:escapeXml(param.q)}उसे एस्केप करते हैं।<%= request.getParameter("q") %>जैसे scriptlet कभी एस्केप नहीं करते। Encode.forHtml / forJavaScript- OWASP Java Encoder में हर आउटपुट संदर्भ के लिए एक मेथड है: element सामग्री के लिए
Encode.forHtml, कोट वाले एट्रिब्यूट के लिएforHtmlAttribute, JavaScript string literal के लिएforJavaScriptऔर URL पैरामीटर के लिएforUriComponent। स्क्रिप्ट के अंदर रखे मान को सिर्फ़ HTML-एनकोड करना काफ़ी नहीं है। OWASP Java HTML Sanitizer- यूज़र के दिए HTML के लिए
Sanitizers.FORMATTING.and(Sanitizers.LINKS)जैसी policy टैग और एट्रिब्यूट की अनुमति-सूची रखती है और स्क्रिप्ट, इवेंट हैंडलर वjavascript:URL हटा देती है। एनकोडिंग टैग को टेक्स्ट की तरह दिखाती; सैनिटाइज़ करना सुरक्षित टैग को काम करने देता है। @ResponseBody returning text/htmlproduces = text/htmlके साथStringलौटाने वाला@RestControllerमेथड रिस्पॉन्स में जैसा है वैसा लिखा जाता है: कोई टेम्पलेट इंजन नहीं चलता, इसलिए कुछ भी एस्केप नहीं होता। view टेम्पलेट इस्तेमाल करें, या जोड़े गए हर मान को एनकोड करें।
फ्रेमवर्क मैपिंग
- इस गाइड में बताई गई कमजोरी के लिए CWE संदर्भ: CWE-79
- OWASP Top 10:2025 — A05:2025 Injection
हमले का चरण-दर-चरण प्रवाह
इनपुट रिक्वेस्ट पैरामीटर में आता है
Spring query string या फ़ॉर्म body से q, user या bio को String आर्ग्युमेंट से बाँधता है। पीड़ित को तैयार किया लिंक भेजा जा सकता है, या भेजी गई bio दूसरे यूज़र्स को दिखती है।
स्ट्रिंग जोड़कर रिस्पॉन्स बनता है
controller मान को HTML या <script> ब्लॉक में चिपका देता है। हानिरहित टेस्ट मान <img src=x onerror=alert(1)> के साथ लौटाई स्ट्रिंग में असली टैग होता है; ';alert(1);// के साथ स्क्रिप्ट ब्लॉक में नया स्टेटमेंट जुड़ जाता है।
ब्राउज़र उसे पार्स करता है
क्योंकि मेथड text/html घोषित करता है, ब्राउज़र body को पेज की तरह रेंडर करता है। इमेज फ़ेल होकर onerror चलाती है, या स्क्रिप्ट ब्लॉक डाला गया स्टेटमेंट चला देता है।
स्क्रिप्ट वेबसाइट बनकर चलती है
कोड ऐप्लिकेशन के origin के साथ चलता है: DOM पढ़ सकता है, पीड़ित की सेशन cookie के साथ ऐप्लिकेशन के endpoint बुला सकता है और यूज़र को दिखने वाली चीज़ें बदल सकता है।
सोर्स कोड: कमज़ोर बनाम सुरक्षित कार्यान्वयन
// ProfileController.java: हर endpoint रिक्वेस्ट डेटा को HTML में जोड़ता है
@RestController
public class ProfileController {
// HTML संदर्भ: query का मान मार्कअप बनकर डलता है
@GetMapping(value = "/search", produces = MediaType.TEXT_HTML_VALUE)
public String search(@RequestParam String q) {
return "<h1>Results for " + q + "</h1>";
}
// JavaScript संदर्भ: user में कोट string literal को ख़त्म कर देता है
@GetMapping(value = "/widget", produces = MediaType.TEXT_HTML_VALUE)
public String widget(@RequestParam String user) {
return "<script>const user = '" + user + "';</script>";
}
// रिच टेक्स्ट सारे टैग और हैंडलर समेत लौट आता है
@PostMapping(value = "/preview", produces = MediaType.TEXT_HTML_VALUE)
public String preview(@RequestParam String bio) {
return "<div class=\"bio\">" + bio + "</div>";
}
}
// ProfileController.java: हर मान अपने संदर्भ के लिए एनकोड या सैनिटाइज़ होता है
@RestController
public class ProfileController {
private static final PolicyFactory BIO_POLICY = Sanitizers.FORMATTING.and(Sanitizers.LINKS);
// forHtml element सामग्री के लिए < > & " ' एनकोड करता है
@GetMapping(value = "/search", produces = MediaType.TEXT_HTML_VALUE)
public String search(@RequestParam String q) {
return "<h1>Results for " + Encode.forHtml(q) + "</h1>";
}
// forJavaScript कोट, बैकस्लैश और </script> क्रम एस्केप करता है
@GetMapping(value = "/widget", produces = MediaType.TEXT_HTML_VALUE)
public String widget(@RequestParam String user) {
return "<script>const user = '" + Encode.forJavaScript(user) + "';</script>";
}
// सैनिटाइज़र फ़ॉर्मेटिंग और लिंक रखता है, बाक़ी सब हटा देता है
@PostMapping(value = "/preview", produces = MediaType.TEXT_HTML_VALUE)
public String preview(@RequestParam String bio) {
return "<div class=\"bio\">" + BIO_POLICY.sanitize(bio) + "</div>";
}
}
इंजीनियरिंग और सिस्टम सुरक्षा चेकलिस्ट
- ✓SpotBugs में Find Security Bugs चालू करें और
XSS_SERVLET,XSS_JSP_PRINTवXSS_REQUEST_PARAMETER_TO_SERVLET_WRITERपर बिल्ड फ़ेल करें। - ✓CI में टेम्पलेट के अंदर
th:utext,[(,<%=और JSP में खुले${...}खोजें; हर मिलान सैनिटाइज़ की हुई या स्थिर सामग्री ही दिखाए। - ✓जब भी controller ख़ुद मार्कअप बनाए, OWASP Java Encoder से सटीक संदर्भ के लिए एनकोड करें (
forHtml,forHtmlAttribute,forJavaScript,forUriComponent)। - ✓यूज़र के दिए HTML को
th:utextया रिस्पॉन्स body तक पहुँचने से पहले OWASP Java HTML Sanitizer policy से गुज़ारें। - ✓Spring Security के
headers().contentSecurityPolicy(...)में nonce-आधारितscript-srcसेट करें और सेशन cookie कोHttpOnlyवSameSite=Laxरखें।