非SpringBoot环境下Spring MVC4及JSON应用的XSS防护方案问询
Hey there, let's tackle your XSS protection questions for your vanilla Spring MVC 4 app (no Spring Boot) — I’ve got practical, framework-native solutions for both regular requests and raw JSON payloads:
You don’t need Spring Boot-specific tools here; Spring MVC 4 has built-in hooks to intercept and sanitize input. Here are the most reliable approaches:
Option 1: Use a HandlerInterceptor with a Request Wrapper
Create an interceptor to wrap the request and sanitize all parameters before they reach your controllers:
First, the request wrapper that overrides parameter retrieval to clean values (using OWASP Java Encoder for safe HTML encoding):
public class XssRequestWrapper extends HttpServletRequestWrapper { public XssRequestWrapper(HttpServletRequest request) { super(request); } @Override public String getParameter(String name) { String value = super.getParameter(name); return sanitize(value); } @Override public String[] getParameterValues(String name) { String[] values = super.getParameterValues(name); if (values == null) return null; for (int i = 0; i < values.length; i++) { values[i] = sanitize(values[i]); } return values; } private String sanitize(String value) { return value != null ? Encoder.encodeForHtml(value) : null; } }
Then the interceptor to apply this wrapper:
public class XssInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // Replace original request with sanitized wrapper HttpServletRequest sanitizedRequest = new XssRequestWrapper(request); request = sanitizedRequest; return super.preHandle(sanitizedRequest, response, handler); } }
Register the interceptor in your spring-servlet.xml:
<mvc:interceptors> <bean class="com.yourpackage.interceptors.XssInterceptor" /> </mvc:interceptors>
Option 2: Custom Property Editors for Controller Binding
If you want sanitization to happen automatically when binding request parameters to your model objects, use a @ControllerAdvice to register a custom property editor:
@ControllerAdvice public class XssBindingAdvice { @InitBinder public void initBinder(WebDataBinder binder) { binder.registerCustomEditor(String.class, new PropertyEditorSupport() { @Override public void setAsText(String text) { // Sanitize string values before binding setValue(text != null ? Encoder.encodeForHtml(text) : null); } }); } }
Just make sure your component scan includes the package where this advice lives.
Since you’re dealing with raw JSON (not form parameters), we need to hook into Spring’s JSON message conversion process. No JsonComponent (a Spring Boot feature) required — we’ll use Jackson’s native modules instead.
Step 1: Sanitize JSON Request Bodies
Create a custom Jackson deserializer for String fields that either sanitizes or blocks XSS content:
Option A: Sanitize Automatically
public class XssStringDeserializer extends StdDeserializer<String> { public XssStringDeserializer() { super(String.class); } @Override public String deserialize(JsonParser parser, DeserializationContext context) throws IOException { String value = parser.getValueAsString(); return value != null ? Encoder.encodeForHtml(value) : null; } }
Option B: Throw Error on XSS Detection
If you want to reject requests with malicious content instead of sanitizing, use OWASP’s Validation library to check for XSS patterns:
public class XssBlockingDeserializer extends StdDeserializer<String> { private final Validator owaspValidator = new Validator(); public XssBlockingDeserializer() { super(String.class); } @Override public String deserialize(JsonParser parser, DeserializationContext context) throws IOException { String value = parser.getValueAsString(); if (value != null && !owaspValidator.isValid(value, ValidationTarget.HTML)) { throw new IOException("Potential XSS content detected in request"); } return value; } }
Step 2: Sanitize JSON Responses
To protect your JSON output, create a corresponding serializer to encode string values:
public class XssStringSerializer extends StdSerializer<String> { public XssStringSerializer() { super(String.class); } @Override public void serialize(String value, JsonGenerator generator, SerializerProvider provider) throws IOException { if (value != null) { generator.writeString(Encoder.encodeForHtml(value)); } else { generator.writeNull(); } } }
Step 3: Register the Jackson Module
Package the deserializer/serializer into a Jackson module and wire it into Spring’s message converter:
public class XssJacksonModule extends SimpleModule { public XssJacksonModule() { // Use XssBlockingDeserializer instead if you want to reject bad requests addDeserializer(String.class, new XssStringDeserializer()); addSerializer(String.class, new XssStringSerializer()); } }
Update your spring-servlet.xml to use this custom module in the JSON converter:
<mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="com.fasterxml.jackson.databind.ObjectMapper"> <property name="modules"> <list> <bean class="com.yourpackage.jackson.XssJacksonModule" /> </list> </property> </bean> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>
This will apply XSS protection to all JSON request bodies and responses globally.
Alternative: Raw JSON Request Wrapper
If you prefer not to modify Jackson’s configuration, you can wrap the request to read and sanitize the entire JSON payload upfront:
public class XssJsonRequestWrapper extends HttpServletRequestWrapper { private final byte[] sanitizedBody; public XssJsonRequestWrapper(HttpServletRequest request) throws IOException { super(request); // Read original JSON body String originalBody = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding()); // Sanitize string values in JSON (use a proper JSON parser instead of regex for complex structures) String sanitizedBodyStr = originalBody.replaceAll("<", "<").replaceAll(">", ">"); sanitizedBody = sanitizedBodyStr.getBytes(request.getCharacterEncoding()); } @Override public ServletInputStream getInputStream() throws IOException { return new ServletInputStream() { private final ByteArrayInputStream bis = new ByteArrayInputStream(sanitizedBody); @Override public int read() throws IOException { return bis.read(); } @Override public boolean isFinished() { return bis.available() == 0; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener listener) {} }; } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream(), getCharacterEncoding())); } }
Then update your XssInterceptor to apply this wrapper to JSON requests:
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String contentType = request.getContentType(); if (contentType != null && contentType.contains("application/json")) { request = new XssJsonRequestWrapper(request); } else { request = new XssRequestWrapper(request); } return super.preHandle(request, response, handler); }
Key Notes:
- Always use trusted libraries like OWASP Java Encoder and OWASP Validation instead of writing your own regex — they cover edge cases you might miss.
- For JSON, the Jackson module approach is more maintainable than raw string replacement, as it properly handles nested structures and string escaping.
内容的提问来源于stack exchange,提问作者Berlin Brown

