In Spring Boot, we can use annotations to defend XSS. Annotations are a lightweight defense that can verify inputs at the method or field level, thus preventing XSS attacks.
If you want to XSS defense for global requests, you can use filters in servlets or interceptors in spring mvc. Here you use filters in servlets for demonstration.
Introduce related dependencies
maven dependencies:
<!--JSR-303/JSR-380Annotations for verification --> <dependency> <groupId></groupId> <artifactId>spring-boot-starter-validation</artifactId> <version>2.6.7</version> </dependency>
If you are using grade, introduce dependencies:
implementation ':spring-boot-starter-validation:2.6.7'
Modify the configuration file
xss: enabled: true excludeUrlList: - /xss/local/test
Define the attribute class corresponding to the configuration file
package ; import ; import ; @Data public class XssFilterProperties { /** * Whether to enable XSS filtering. */ private boolean enabled = true; /** * URL patterns that need to be excluded, these URLs will not be XSS filtered. */ private List<String> excludeUrlList; }
Inject XSS configuration class
package ; import ; import ; import ; import ; import ; import ; import ; import ; @Data @Configuration public class XssFilterConfig { @ConfigurationProperties(prefix = "xss") @Bean public XssFilterProperties xssFilterProperties() { return new XssFilterProperties(); } /** * Register XSS filters. * * @return FilterRegistrationBean is used to register filters. */ @Bean public FilterRegistrationBean<XssFilter> xssFilterRegistration(XssFilterProperties xssFilterProperties) { FilterRegistrationBean<XssFilter> registrationBean = new FilterRegistrationBean<>(); // Set the distribution type of the filter to the request type (); // Create an instance of XssFilter (new XssFilter(xssFilterProperties)); // Add a URL pattern that needs to be intercepted by filters, and all requests are intercepted here ("/*"); // Set the name of the filter ("XssFilter"); // Set the execution order of the filter. The smaller the value, the higher the priority. (9999); return registrationBean; } @Bean public HttpMessageConverters xssHttpMessageConverters() { XSSMappingJackson2HttpMessageConverter xssMappingJackson2HttpMessageConverter = new XSSMappingJackson2HttpMessageConverter(); HttpMessageConverter converter = xssMappingJackson2HttpMessageConverter; return new HttpMessageConverters(converter); } }
XssFilter Filter
The XssFilter filter wraps all requests that need to be defended as XssWrapper.
package ; import .slf4j.Slf4j; import ; import .*; import ; import ; import ; import ; import ; @Slf4j public class XssFilter implements Filter { private final XssFilterProperties xssFilterProperties; public XssFilter(XssFilterProperties xssFilterProperties) { = xssFilterProperties; } /** * Execute filtering logic, if the current request is not in the exclusion list, wrap the request through an XSS filter. * * @param request HTTP request object. * @param response HTTP response object. * @param chain filter chain object, used to continue or interrupt request processing. * @throws IOException If an I/O error occurs during processing. * @throws ServletException If a Servlet-related error occurs during processing. */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; //If the access interface is in the exclusion list, it will not be blocked if (isExcludeUrl(())) { (request, response); return; } ("uri:{}", ()); // xss filter (new XssWrapper(req), resp); } /** * Determine whether the currently requested URL should be excluded from XSS filtering. * * @param urlPath URL path to request. * @return Return true if the request should be excluded; otherwise return false. */ private boolean isExcludeUrl(String urlPath) { if (!()) { //If the xss switch is turned off, all urls will not be blocked return true; } if((())) { return false; } for (String pattern : ()) { Pattern p = ("^" + pattern); Matcher m = (urlPath); if (()) { return true; } } return false; } }
XssWrapper filters get request and request header
XssWrapper filters illegal characters in get request and request header.
package ; import .slf4j.Slf4j; import .; import ; import ; @Slf4j public class XssWrapper extends HttpServletRequestWrapper { public XssWrapper(HttpServletRequest request) { super(request); } /** * Special character filtering of array parameters */ @Override public String[] getParameterValues(String name) { String[] values = (name); if (values == null) { return null; } int count = ; String[] encodedValues = new String[count]; for (int i = 0; i < count; i++) { encodedValues[i] = (values[i]); } return encodedValues; } /** * Filter special characters in the parameters */ @Override public String getParameter(String name) { String value = (name); if ((value)) { return value; } return (value); } /** * Get attribute, special character filtering */ @Override public Object getAttribute(String name) { Object value = (name); if (value instanceof String && ((String) value)) { return ((String) value); } return value; } /** * Special character filtering on request header */ @Override public String getHeader(String name) { String value = (name); if ((value)) { return value; } return (value); } }
MessageConverter filters post requests
package ; import ; import ; import ; import ; import ; import .MappingJackson2HttpMessageConverter; import ; import ; /** * Message parser that avoids xss attacks when reading and writing JSON data * */ public class XSSMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter { /** * Read objects from HTTP input messages while applying XSS protection. * * @param type type token, indicating the type of object to be read. * @param contextClass context class, providing context information for type parsing. * @param inputMessage HTTP input message containing the JSON data to be read. * @return The object parsed from the input message is processed by XSS protection. * @throws IOException If an I/O error occurs. * @throws HttpMessageNotReadableException If the message cannot be read. */ @Override public Object read(Type type, Class contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { JavaType javaType = getJavaType(type, contextClass); Object obj = readJavaType(javaType, inputMessage); //Get request json String json = ().writeValueAsString(obj); //Filter special characters String result = (json); Object resultObj = ().readValue(result, javaType); return resultObj; } /** * Read an object of the specified Java type from an HTTP input message, and is used internally. * * @param javaType Java type of the object to be read. * @param inputMessage HTTP input message containing the JSON data to be read. * @return Object parsed from the input message. * @throws IOException If an I/O error occurs. * @throws HttpMessageNotReadableException If the message cannot be read. */ private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) { try { return ().readValue((), javaType); } catch (IOException ex) { throw new HttpMessageNotReadableException("Could not read JSON: " + (), ex); } } /** * Write objects to HTTP output messages while applying XSS protection. * * @param object The object to be written. * @param outputMessage HTTP output message, the object will be serialized to JSON and written to this message. * @throws IOException If an I/O error occurs. * @throws HttpMessageNotWritableException If the message cannot be written. */ @Override protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { //Get the json to be output String json = ().writeValueAsString(object); //Filter special characters String result = (json); // Output ().write(()); } }
Xss filter tool class
package ; import ; import ; import ; /** * XSS filtering tool class, using Jsoup library to protect the input strings XSS attack */ public class XssUtil { /** * Use the relaxed whitelist that comes with jsoup */ private static final Whitelist WHITE_LIST = (); /** * Define the output settings and turn off prettyPrint (prettyPrint=false), the purpose is to avoid formatting the code during the cleaning process. * This keeps the consistency of input and output content. */ private static final OUTPUT_SETTINGS = new ().prettyPrint(false); /* Initialize the whitelisting policy, allowing all tags to have style attributes. This is because in rich text editing, styles are usually defined by the style attribute, and it is necessary to ensure that these styles can be preserved. */ static { // Some styles are implemented using style when editing rich text // For example, red font style="color:red;" // So you need to add style attribute to all tags WHITE_LIST.addAttributes(":all", "style"); } /** * Clean the entered string and remove potential XSS attack code. * * @param content The string to be cleaned is usually the HTML content entered by the user. * @return The cleaned string is guaranteed to not contain XSS attack code. */ public static String clean(String content) { // Use defined whitelist strategy and output settings to clean the input string return (content, "", WHITE_LIST, OUTPUT_SETTINGS); } }
get request test
package ; import ; import ; import ; import ; /** * Xss2 defense get request */ @RestController @RequestMapping("/xss/global") @Validated public class XssGlobalGetController { /** * Use annotations to intercept xss in the get request, add @Xss before the method parameters, and note that the @Validated annotation should be added to the class. * * @param userAccount Request parameters * @return Request Parameters */ @GetMapping("/test") public String test(String userAccount) { return userAccount; } }
Send a get request: http://localhost:8888/xss/global/test?userAccount=demoData
Return result: demoData
Post request test
package ; import ; import ; import ; import ; /** * Xss global defense post request */ @RestController @RequestMapping("/xss/global") public class XssGlobalPostController { /** * Use annotations to intercept xss in POST requests, and add @Xss or @Validated annotations to the attributes that need to be intercepted by the entity class. * * @param userGlobalLoginPojo Entity Class * @return Entity Class */ @PostMapping("/test") public UserGlobalLoginPojo test(@RequestBody UserGlobalLoginPojo userGlobalLoginPojo) { return userGlobalLoginPojo; } }
Send post request: http://localhost:8888/xss/global/test
Request body:
{
"userAccount": "<iframe οnlοad='alert(0)'>demoData</iframe>"
}
Return result:
{
"userAccount": "demoData"
}
This is the end of this article about how SpringBoot uses filters to defend XSS. For more related SpringBoot XSS defense content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!