To avoid XSS, I suggest that escaping be done when outputting data, since proper escaping depends on the output.
If the JSON response generated by @ResponseBody is consumed directly by the client and there is no way for XSS to escape the content, then JacksonMessageConverter can be configured to perform XSS escaping in strings.
You can configure JacksonMessageConverter as follows:
1) First we create an ObjectMapper factory that will create our custom mapper object:
public class HtmlEscapingObjectMapperFactory implements FactoryBean<ObjectMapper> { private final ObjectMapper objectMapper; public HtmlEscapingObjectMapperFactory() { objectMapper = new ObjectMapper(); objectMapper.getJsonFactory().setCharacterEscapes(new HTMLCharacterEscapes()); } @Override public ObjectMapper getObject() throws Exception { return objectMapper; } @Override public Class<?> getObjectType() { return ObjectMapper.class; } @Override public boolean isSingleton() { return true; } public static class HTMLCharacterEscapes extends CharacterEscapes { private final int[] asciiEscapes; public HTMLCharacterEscapes() {
(inspiration for HtmlCharacterEscapes came from this question: HTML output using Spring MVC and Jackson Mapper )
2) Then we register a message converter that uses our custom object mapping (example in xml config):
<bean id="htmlEscapingObjectMapper" class="com.example.HtmlEscapingObjectMapperFactory" /> <mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" p:objectMapper-ref="htmlEscapingObjectMapper" /> </mvc:message-converters> </mvc:annotation-driven>
Now all JSON messages created by @ResponseBody should have strings escaped as specified in HTMLCharacterEscapes.
Alternative solutions to the problem:
- XSS removes what you need in the body of the controller after the objects have been deserialized
- possibly XSS javascript escape on client before outputting content
In addition to performing output escaping, it may also be useful to do some input validation (using standard Spring validation methods) to block some of the content that you do not want to enter into the system / database.
EDIT: JavaConfig
I have not tried this, but in Java configuration it should work as follows (you will not need a factory Bean from above, because in this case you can configure everything in the configuration):
@Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { super.configureMessageConverters(converters); converters.add(buildHtmlEscapingJsonConverter()); } private MappingJacksonHttpMessageConverter buildHtmlEscapingJsonConverter() { MappingJacksonHttpMessageConverter htmlEscapingConverter = new MappingJacksonHttpMessageConverter(); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.getJsonFactory().setCharacterEscapes(new HTMLCharacterEscapes()); htmlEscapingConverter.setObjectMapper(objectMapper); return htmlEscapingConverter; }
Keep in mind that any other default non-json message converters that will normally be configured will now be lost (for example, XML converters, etc.), and if you need them, you will need to add them manually (you you can see that active is the default here in section 2.2: http://www.baeldung.com/spring-httpmessageconverter-rest )