4

I am attempting to use a filter to check for HTML tags in a response body. The problem is that if I alter the body in the filter, it isn't altered when it gets to the client. I tried the solution shown here: Looking for an example for inserting content into the response using a servlet filter but it didn't help.

I have two filters. SecureWrapperFilter wraps the request/response objects in our custom wrapper, and XSSFilter uses OWASP encode to encode for html content. The filters look like this:

public class SecureWrapperFilter implements Filter {

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

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response,
        final FilterChain chain) throws IOException, ServletException
    {
        final ServletRequestWrapper securityRequest =
            new ServletRequestWrapper((HttpServletRequest)request);
        final ServletResponseWrapper securityResponse =
            new ServletResponseWrapper((HttpServletResponse)response);
        ESAPI.httpUtilities().setCurrentHTTP(securityRequest, securityResponse);
        chain.doFilter(ESAPI.currentRequest(), ESAPI.currentResponse());
    }

    @Override
    public void destroy() {
    }
}

and:

public class XSSFilter implements Filter {

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

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response,
        final FilterChain chain) throws IOException, ServletException
    {      
        final ServletRequestWrapper requestWrapper = (ServletRequestWrapper)request;
        final String body = Encode.forHtmlContent(requestWrapper.getBody());
        requestWrapper.setBody(body);
        chain.doFilter(requestWrapper, response);
        final ServletResponseWrapper responseWrapper = (ServletResponseWrapper)response;
        final byte[] copy = responseWrapper.getCopy();
        final String oldBody = new String(copy, response.getCharacterEncoding());
        final String newBody = Encode.forHtmlContent(oldBody);
        if (!StringUtils.equals(oldBody, newBody)) {
            responseWrapper.getResponse().getOutputStream().write(newBody.getBytes());
        }
    }

    @Override
    public void destroy() {
    }
}

If I add some debug Logging, I can see that the securityResponse has the modified body in the SecureWrapperFilter, but on the client side, the body looks as if it was never modified.

Any suggestions would be greatly appreciated. Thanks.

Community
  • 1
  • 1
Ben Green
  • 3,953
  • 3
  • 29
  • 49

3 Answers3

2

The problem was that in my XSSFilter, I was appending the new response body onto the old one. This was causing invalid json like {"x"="y"}{"escapedx"="escapedy")

Our client deserializer was only printing the first json object so {"x"=y"} was all we were seeing on the client side.

To resolve this problem, I added the following line to the XSSFilter:

responseWrapper.getResponse().resetBuffer();

before

responseWrapper.getResponse().getOutputStream().write(newBody.getBytes());

This clears the buffer, allowing me to rewrite it on the line below. My json on the client side now looks like: {"escapedx"="escapedy"}

Ben Green
  • 3,953
  • 3
  • 29
  • 49
1

You need to make sure the HttpResponse is buffered. If the buffer is not big enough, then the reponse will be streamed to the client befire your filter is called.

Or maybe the servler is calling flush() on the response?

David Roussel
  • 5,788
  • 1
  • 30
  • 35
  • Thanks for the response. I have just added `responseWrapper.flushBuffer()` to the end of the XSSFilter, but it hasn't made a difference. That's the only place where flushBuffer() is being called. – Ben Green Jun 27 '14 at 14:18
  • before you call doFilter(), call "res.setBufferSize(8 * 1024); // 8K buffer", you may need more than 8K. You need enough to buffer your whole response. – David Roussel Jun 27 '14 at 15:41
  • I have tried this, and it has't fixed the problem. My response body is only very small, so I doubt this is to blame. I'm wondering now if the javax rest response flushes the buffer before it even hits my filters. – Ben Green Jun 28 '14 at 09:01
  • Thanks for the help. I have found the problem, and thanks for spurring me to take another look at the buffers. – Ben Green Jun 30 '14 at 08:47
0

Sending back json can be done with jackson:

val res = response as HttpServletResponse

res.status = HttpStatus.UNAUTHORIZED.value()
res.contentType = MediaType.APPLICATION_JSON_UTF8_VALUE

res.outputStream.write(ObjectMapper().writeValueAsString(ResponseError(
    "two_factor_auth_failed", "Two Factor Authorization is required to proceed."
)).toByteArray())
shredding
  • 5,374
  • 3
  • 46
  • 77