【问题标题】:@InitBinder with @RequestBody escaping XSS in Spring 3.2.4@InitBinder 与 @RequestBody 在 Spring 3.2.4 中转义 XSS
【发布时间】:2014-10-13 17:59:15
【问题描述】:

我的方法中有一个 @RequestBody 注释参数,如下所示:

@RequestMapping(value = "/courses/{courseId}/{name}/comment", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody CommentContainer addComment(@PathVariable Long courseId,
                          @ActiveAccount Account currentUser,
                          @Valid @RequestBody AddCommentForm form,
                          BindingResult formBinding,
                          HttpServletRequest request) throws RequestValidationException {

.....
}

然后我在同一个控制器中有一个@InitBinder注解的方法:

@InitBinder
public void initBinder(WebDataBinder dataBinder) {
    dataBinder.registerCustomEditor(AddCommentForm.class, new StringEscapeEditor());
}

我的StringEscapeEditor 没有运行。但我的initBinder 方法是。所以它不会将我的表单映射到转义编辑器。这似乎是在阅读此线程之后(@InitBinder 似乎不支持@RequestMapping):

spring mvc @InitBinder is not called when processing ajax request

我测试了映射@PathVariable 字符串,然后我的编辑器正在工作。

这在我的应用程序中很重要,因为我的大部分绑定都是使用 @RequestBody 完成的,如果我可以对其应用一些自定义绑定,那就太好了。

解决此问题的最常用方法是什么?并转义我的输入数据以进行脚本攻击。

【问题讨论】:

  • @InitBinder 不适用于@RequestBody。与@ModelAttribute 进行数据绑定时使用绑定器。 @RequestBody 由 Jackons 或 XML 解析器解析,或者...取决于请求的类型。如果你想要自定义绑定配置杰克逊。
  • 好的,谢谢您的确认。我想我可以把@RequestBody 扔进垃圾箱,然后通过我的ajax 调用发送@ModelAttribute
  • 否...@ModelAttribute 可用于绑定请求参数而不是请求正文。
  • 我建议在输出数据而不是在输入时转义,因为转义方法因您输出的文档类型而异(例如,对于 html 和 csv,它是不同的)。如果您真的想在输入时使用@ResponseBody,那么请考虑将 Jackson 配置为它。
  • @M.Deinum 我的意思是我可以删除 @RequestBody AddCommentForm form 并映射类似 @ModelAttribute("addCommentForm") AddCommentForm form 的东西,然后我可以使用带有一些自定义映射的 initbinder。

标签: ajax spring spring-mvc spring-data


【解决方案1】:

为了逃避 XSS,我建议在输出数据时进行转义,因为正确的转义取决于输出文档。

如果@ResponseBody生成的JSON响应被客户端直接消费,没有机会对内容进行XSS转义,那么可以自定义JacksonMessageConverter对字符串进行XSS转义。

可以像这样自定义 JacksonMessageConverter:

1) 首先我们创建 ObjectMapper 工厂,该工厂将创建我们的自定义对象映射器:

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() {
            // start with set of characters known to require escaping (double-quote, backslash etc)
            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;
        }


        @Override
        public int[] getEscapeCodesForAscii() {
            return asciiEscapes;
        }

        // and this for others; we don't need anything special here
        @Override
        public SerializableString getEscapeSequence(int ch) {
            return new SerializedString(StringEscapeUtils.escapeHtml4(Character.toString((char) ch)));

        }
    }
}

(HtmlCharacterEscapes 的灵感来自这个问题:HTML escape with Spring MVC and Jackson Mapper

2)然后我们注册使用我们自定义对象映射器的消息转换器(xml配置中的示例):

<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>

现在,@ResponseBody 创建的所有 JSON 消息都应该按照 HTMLCharacterEscapes 中的规定进行转义。

问题的替代解决方案:

  • 反序列化对象后,XSS 会在控制器主体中转义您需要的内容
  • 可能在输出内容之前在客户端的 javascript 中进行 XSS 转义

除了做输出转义之外,做一些输入验证(使用标准的 Spring 验证方法)来阻止一些你不想输入到系统/数据库中的内容可能会很有用。

编辑:JavaConfig

我还没有尝试过,但在 Java 配置中它应该像这样工作(你不需要上面的 Factory Bean,因为在这种情况下你可以在配置中设置所有内容):

@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;       
}

请注意,通常配置的任何其他非 json 默认消息转换器现在都将丢失(例如 XML 转换器等),如果您需要它们,则需要手动添加它们(您可以查看在第 2.2 节中默认处于活动状态:http://www.baeldung.com/spring-httpmessageconverter-rest)

【讨论】:

  • 你知道如何在 Java WebMvcConfig 中进行配置吗?有问题把它做好。我可以在 java mvc 配置中做@Override public void configureMessageConverters( List&lt;HttpMessageConverter&lt;?&gt;&gt; converters ) { converters.add(CONVERTER_GOES_HERE);} 但不确定如何定义转换器
  • 已编辑以包含 java 配置指南。
  • 它似乎不适用于 MappingJackson2HttpMessageConverter?有其他选择吗?
  • 对于未来的访问者:使用 Jackson2ObjectMapperFactoryBean 进行 MappingJackson2HttpMessageConverter。
  • 如何在 JSP 的 json 打印中转义 html?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-09
  • 2012-10-08
  • 2015-01-26
  • 2012-06-30
  • 2014-07-26
相关资源
最近更新 更多