Java覆盖来自HttpServletRequestWrapper的getInputStream以在JSON中转义HTML

时间:2015-02-10 00:43:45

标签: jackson xss

我试图覆盖HttpServletRequestWrapper#getInputStream()。我试图读取正文中的JSON并转义HTML标记以防止XSS。我正在研究具有多个端点的大型应用程序。我正在使用JacksonMapper将JASON转换为POJO,因此希望在实际映射之前清理输入。我的代码看起来像

    @Override
    public ServletInputStream getInputStream() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    Map<String, Object> jsonMap = mapper.readValue(super.getInputStream(),
            Map.class);

    for (String key : jsonMap.keySet()) {
        if (jsonMap.get(key) != null) {
            if (jsonMap.get(key).getClass() == String.class)
                jsonMap.put(key, (Object) StringEscapeUtils.escapeHtml((String) jsonMap.get(key)));
        }
    }
    ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
    ObjectOutputStream out = new ObjectOutputStream(byteOut);
    mapper.writeValue(out, jsonMap);
    final ByteArrayInputStream byteIn = new ByteArrayInputStream(
            byteOut.toByteArray());
    ServletInputStream inputStream = new ServletInputStream() {

        @Override
        public int read() throws IOException {
            return byteIn.read();
        }
    };
    return inputStream;
}

我正在获得Jackson Mapping异常,如下所示:

8:29:20,323 WARN  [org.jboss.resteasy.core.SynchronousDispatcher] (http-/0.0.0.0:7080-7) Failed executing PUT /portal/api/tenant/2: org.jboss.resteasy.spi.ReaderException: 
org.codehaus.jackson.JsonParseException: Unexpected character ('¬' (code 172)): 
expected a valid value (number, String, array, object, 'true', 'false' or 'null')                                                                                                                                                                                    
at [Source: com.dell.em.controller.RequestWrapper$1@4ed35486; line: 1, column: 2]                                                                                                                                                                               
    at org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.java:202) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]                                                                                     
    at org.jboss.resteasy.core.MethodInjectorImpl.injectArguments(MethodInjectorImpl.java:136) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]                                                                                                
    at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:159) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]                                                                                                         
    at org.jboss.resteasy.core.ResourceMethod.invokeOnTarget(ResourceMethod.java:269) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]                                                                                                         
    at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:227) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]                

我不确定这个字符的来源。我有一个简单的json作为输入:

{"id":2,"tenantName":"Test","tenantLogo":null,"address1":"<script>alert(\"o1\")</script>","city":"test","state":"Alabama","zip":"78769","country":"United States","timezone":"US/Central","contactFirstName":"Test","contactLastName":"Test","contactEmail":"Test@rest.com","contactPhone":"9794444444","address2":null,"domainName":null}

2 个答案:

答案 0 :(得分:3)

如果有人遇到同样的问题。我不得不编码新的InputStream而不是writeVAlue我需要writeValueAsString来在json周围加上引号。

@Override
public ServletInputStream getInputStream() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    InputStream in = super.getInputStream();
    Map<String, Object> jsonMap = mapper.readValue(in, Map.class);

    for (String key : jsonMap.keySet()) {
        if (jsonMap.get(key) != null) {
            if (jsonMap.get(key).getClass() == String.class)
                jsonMap.put(key, (Object) StringEscapeUtils
                        .escapeHtml((String) jsonMap.get(key)));
        }
    }

    final ByteArrayInputStream byteIn = new ByteArrayInputStream(Charset
            .forName("UTF-16").encode(mapper.writeValueAsString(jsonMap))
            .array());


    ServletInputStream inputStream = new ServletInputStream() {

        @Override
        public int read() throws IOException {
            return byteIn.read();
        }
    };

    return inputStream;
}

答案 1 :(得分:0)

我已经修改了@Rahul Dhar的解决方案以适应我的情况,因为我有几个休息服务接受常规输入和json输入(比如接受正文作为用户的id)

@Override
public ServletInputStream getInputStream() throws IOException {

        ObjectMapper mapper = new ObjectMapper();
        InputStream in = super.getInputStream();
        String inputString = IOUtils.toString(in, "utf-8");

        if (inputString.contains("{")) {    // is JSON
            @SuppressWarnings("unchecked")
            Map<String, Object> jsonMap = mapper.readValue(inputString, Map.class);

            for (String key : jsonMap.keySet()) {
                Object value = jsonMap.get(key);
                if (value != null) {
                    if (value.getClass() == String.class)
                        jsonMap.put(key, (Object) stripXSS((String) value)); // (stripXSS is a private method that do spit out xss characters)
                }
            }
            return new CustomServletInputStream(
                    Charset.forName("utf-8").encode(mapper.writeValueAsString(jsonMap)).array());
        } else {
            return new CustomServletInputStream(Charset.forName("utf-8").encode(inputString).array());
        }
}