2016-11-10 45 views
1

我想系統地解決HTTP響應分裂問題。我開發了一個包裝類HttpServletResponse調用HardenedHttpServletResponse緩解分裂嘗試。如何讓Jersey使用HttpServletResponse包裝調用資源方法?

遺憾的是,我無法通過我的HardenedHttpServletResponse讓澤西島撥打我的資源方法。當我嘗試時,我得到null

下面是一個HTTP響應拆分漏洞其是通過將百分比編碼CRLFs(%0d%0a)在文件名查詢參數利用的一個人爲的JAX-RS資源:

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(); 
    } 
} 

這裏是一個虛擬實現的StreamingOutput,使之成爲全幾分例如:

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(); 
    } 
} 

這裏是HttpServletResponse的包裝類,通過拋出異常減輕HTTP響應拆分,如果它檢測CR或LF字符在標題名稱或值:

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(); 
     } 
    } 
} 

澤西供給的實際響應對象如果響應參數的類型爲@Context HttpServletResponse的,但如果響應參數的類型爲@Context HardenedHttpServletResponse

如何讓澤西電話與HttpServletResponse包裝調用資源方法?

回答

1

您可以通過添加DI系統來使其注射。

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

你需要使課堂public也是它的構造公衆,使DI系統可以創建它。這將允許你注入HardenedHttpServletResponse

參見:

+0

很好的回答。謝謝。我不得不將它修改爲**。proxy(false)**代碼有點適應。它的工作原理和我從特定的和新澤西州的DI中學到了更多關於HK2的知識,我可以在將來的工作中應用它。 –

+0

對不起,我忘了它需要一個參數。它應該是'proxy(true)'。默認情況下它是錯誤的,所以如果你不想要代理服務器,你可以把前兩個鏈接的呼叫留在外面。您希望代理的原因是您需要將其注入單例中,然後將其包裝在線程本地代理中。但是如果它被注入到一個請求作用域對象(而不是單例)中,那麼由於第二次鏈接調用,它不會是代理(真實對象)。這也是他如何配置HttpServletRequest。這就是爲什麼你可以將它注入一個單獨的ContainerRequestFilter –