1

I am trying to systematically address HTTP response splitting. I have developed a wrapper class for HttpServletResponse called HardenedHttpServletResponse that mitigates splitting attempts.

Regrettably, I cannot get Jersey to call my resource method with my HardenedHttpServletResponse. I get nulls when I try.

Here is a contrived JAX-RS resource with a HTTP response splitting vulnerability which is exploitable by putting percent-encoded CRLFs (%0d%0a) in the filename query parameter:

AttachmentResource.java:

import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;

@Path("/attachment")
@Produces(MediaType.APPLICATION_JSON)
public final class AttachmentResource {
    @GET
    @Path("/file")
    public StreamingOutput getAttachment(
        @Context HttpServletResponse response,
        @QueryParam("filename") String filename
    ) throws Exception {
        response.setHeader(
            "content-disposition",
            "attachment; filename=" + filename
        );
        return new DummyStreamingOutput();
    }
}

Here is a dummy implementation of StreamingOutput to make it a somewhat full example:

DummyStreamingOutput.java:

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.StreamingOutput;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;

private static DummyFileStreamingOutput implements StreamingOutput {
    @Override
    public void write(OutputStream outputStream) throws IOException, WebApplicationException {
        String message = "Hello, World!";
        byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
        outputStream.write(bytes);
        outputStream.flush();
        outputStream.close();
    }
}

Here is the HttpServletResponse wrapper class that mitigates HTTP response splitting by throwing an exception if it detects CR or LF characters in header names or values:

HardenedHttpServletResponse.java:

import javax.inject.Inject;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.ws.rs.core.Context;

final class HardenedHttpServletResponse extends HttpServletResponseWrapper {
    @Inject
    HardenedHttpServletResponse(@Context HttpServletResponse response) {
        super(response);
    }

    @Override
    public void setHeader(String name, String value) {
        mitigateResponseSplitting(name);
        mitigateResponseSplitting(value);
        super.setHeader(name, value);
    }

    @Override
    public void addHeader(String name, String value) {
        mitigateResponseSplitting(name);
        mitigateResponseSplitting(value);
        super.setHeader(name, value);
    }

    @Override
    public void setIntHeader(String name, int value) {
        mitigateResponseSplitting(name);
        super.setIntHeader(name, value);
    }

    @Override
    public void setDateHeader(String name, long date) {
        mitigateResponseSplitting(name);
        super.setDateHeader(name, date);
    }

    private void mitigateResponseSplitting(String value) {
        if (value != null && (value.contains("\r") || value.contains("\n"))) {
            throw new HttpResponseSplittingException();
        }
    }
}

Jersey supplies the actual response object if the response parameter has type @Context HttpServletResponse, but null if the response parameter has type @Context HardenedHttpServletResponse.

How do I get Jersey to call a resource method with an HttpServletResponse wrapper?

jwells131313
  • 2,364
  • 1
  • 16
  • 26
Alain O'Dea
  • 21,033
  • 1
  • 58
  • 84

1 Answers1

1

You can just make it injectable by adding it the DI system.

resourceConfig.register(new AbstractBinder() {
    @Override
    public void configure() {
        bindAsContract(HardenedHttpServletResponse.class)
            .proxy(false)
            .proxyForSameScope(false)
            .in(RequestScoped.class);
    }
});

You will need to make the class public and also its constructor public, so that the DI system can create it. This will allow you to inject HardenedHttpServletResponse

See also:

Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • Good answer. Thank you. I had to modify it to be **.proxy(false)** code somewhat to adapt. It works and I've learned a lot more about HK2 in specific and Jersey DI in general from it which I can apply in future work. – Alain O'Dea Nov 11 '16 at 02:03
  • Sorry, I forgot it takes an argument. It should be `proxy(true)`. It's false by default, so if you didn't want a proxy, you could just leave the first two chained calls out. The reason you _would_ want it to proxy is if you need to inject it into a singleton, then it would get wrapped in a thread-local proxy. But if it is being injected into a request scoped object (not singleton), then it would not be a proxy (the real object), because of the second chained call. This is also how he HttpServletRequest is configured. That's why you can inject it into a singletone ContainerRequestFilter – Paul Samsotha Nov 11 '16 at 02:09