Spring Boot escape characters at Request Body for XSS protection

雨燕双飞 提交于 2020-06-28 06:19:20

问题


I'm trying to secure my spring boot application using a XSSFilter like this:

public class XSSFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException { }

    @Override
    public void destroy() { }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        chain.doFilter(new XSSRequestWrapper((HttpServletRequest) request), response);
    }

}

And the wrapper:

public class XSSRequestWrapper extends HttpServletRequestWrapper {

    public XSSRequestWrapper(HttpServletRequest servletRequest) {
        super(servletRequest);
    }

    @Override
    public String[] getParameterValues(String parameter) {
        String[] values = super.getParameterValues(parameter);

        if (values == null) {
            return null;
        }

        int count = values.length;
        String[] encodedValues = new String[count];
        for (int i = 0; i < count; i++) {
            encodedValues[i] = replaceXSSCharacters((values[i]));
        }

        return encodedValues;
    }

    private String replaceXSSCharacters(String value) {
        if (value == null) {
            return null;
        }

        return value
                .replace("&","&#38;")
                .replace("<", "&#60;")
                .replace(">","&#62;")
                .replace("\"","&#34;")
                .replace("'","&#39;");
    }

    @Override
    public String getParameter(String parameter) {
        return replaceXSSCharacters(super.getParameter(parameter));
    }

    @Override
    public String getHeader(String name) {
        return replaceXSSCharacters(super.getHeader(name));
    }

}

The problem is, that only secures the Request parameters and Headers, not the Request body, and sometimes my Controller receive data using @RequestBody.

So, if i submit to my controller a json like this:

{"name":"<script>alert('hello!')</script>"}

The html chars at the name property doesn't get escaped like i need. How can i escape the RequestBody?

EDIT: This is different from the "duplicated" question. My question is very Specific. How to escape characters on Request Body.


回答1:


I resolved with a custom class:

@Configuration
public class AntiXSSConfig  {

    @Autowired()
    public void configeJackson(ObjectMapper mapper) {
        mapper.getFactory().setCharacterEscapes(new HTMLCharacterEscapes());
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    public static class HTMLCharacterEscapes extends JsonpCharacterEscapes {

        @Override
        public int[] getEscapeCodesForAscii() {
            int[] asciiEscapes = CharacterEscapes.standardAsciiEscapesForJSON();
            // and force escaping of a few others:
            asciiEscapes['<'] = CharacterEscapes.ESCAPE_CUSTOM;
            asciiEscapes['>'] = CharacterEscapes.ESCAPE_CUSTOM;
            asciiEscapes['&'] = CharacterEscapes.ESCAPE_CUSTOM;
            asciiEscapes['"'] = CharacterEscapes.ESCAPE_CUSTOM;
            asciiEscapes['\''] = CharacterEscapes.ESCAPE_CUSTOM;
            return asciiEscapes;
        }

        @Override
        public SerializableString getEscapeSequence(int ch) {
            switch (ch) {
                case '&' : return new SerializedString("&#38;");
                case '<' : return new SerializedString("&#60;");
                case '>' : return new SerializedString("&#62;");
                case '\"' : return new SerializedString("&#34;");
                case '\'' : return new SerializedString("&#39;");
                default : return super.getEscapeSequence(ch);
            }
        }
    }
}

It covers all the cases.




回答2:


  1. Have a local String field in XSSRequestWrapper which holds the cleaned-up body (probably not suitable for large bodies).
  2. Populate this field in the constructor by reading request.getInputStream() and cleaning up the body the same way as parameters.
  3. Override getInputStream and getReader methods of HttpServletRequestWrapper, and construct an InputStream (string -> byte array -> ByteArrayInputStream) and Reader (StringReader) from the String field and return them respectively. Maybe cache the constructed InputStream and Reader objects for better performance for when the methods are called repeatedly.

You may also be interested in cleaning up JSON when it is being deserialized into Java object.




回答3:


To remove XSS characters you just override AbstractJackson2HttpMessageConverter - this converter has responsibility to read request.inputStream to RequestBody object

@Component
public class XSSRequestBodyConverter extends AbstractJackson2HttpMessageConverter {
    public XSSRequestBodyConverter(ObjectMapper objectMapper) {
        super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
    }

@Override
public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage)
        throws IOException, HttpMessageNotReadableException {

    Object requestBody = super.read(type, contextClass, inputMessage);
    //Remove xss from requestBody here
    String requestInStr = objectMapper.writeValueAsString(requestBody);
    return objectMapper.readValue(replaceXSSCharacters(requestInStr), Object.class);
}


}


来源:https://stackoverflow.com/questions/49439020/spring-boot-escape-characters-at-request-body-for-xss-protection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!