-
Notifications
You must be signed in to change notification settings - Fork 6k
Spring Security not writing http headers when the http stream was flushed by a participant of the filterChain. #3975
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
We have also noticed this in our applications and had to revert back to 4.0.3.RELEASE as well. |
Yes, we put a Filter in the chain that writes the headers first (no-cache for example). I will be working in a unit test that probes the issue in the meantime. Thanks Quantas! I'm wondering what was the motivation of swapping the order in which the headers and the filterChain#doFilter happens... |
I think we had a similar thing where we added headers like no-cache. I hope this gets corrected in 4.1.2.RELEASE so we can continue to keep up with updates. |
it seems pretty easy to fix the code to behave as previous version, provided there was no specific reason for this change... Is bit disruptive change that can go unnoticed when upgrading compromising in some way the app security. |
It seems that the HeaderWriterResponse is doing what is supposed to do but at some point it does not participates in the Filter chain from the very beginning so when he starts playing the response could have been already committed by some other party in the chain. |
Unit test that mimics the mentioned behaviour. |
Thanks for the report. This is due to fixes for #2953 This should happen before the response is committed due to using Can anyone provide a complete application (not unit test) where this is happening? |
Hi Rob, of course, I will try to attach the code as soon as I can. |
Hi Rob, I am working on creating an artifact that mimics our app so the issue can be reproduced. In the meantime, I would expect an exception (like an assertion) to be thrown instead of just not interfering with the flow. That way you can be aware the code could be vulnerable due to missing headers sent to the client and verify the config or apply a workaround. |
@jtoselli I don't think we want an exception in this case. If this were something that happened, it would be something provided by the servlet container |
@rwinch were you able to reproduce the issue? I'm sorry I didn't have the time to upload my config yet. Let me know if you still need it and I will do my best :) |
@jtoselli Thanks for reaching out. I haven't been able to reproduce this. I'm asking for a full integration test because the unit test is invalid. This is because the unit test performs a For example, the following test is more realistic and passes: package com.example;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.web.header.HeaderWriter;
import org.springframework.security.web.header.HeaderWriterFilter;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@RunWith(MockitoJUnitRunner.class)
public class HeaderWriterFilterNotWrittingHeadersTest {
@Mock
HeaderWriter writer;
HeaderWriterFilter subject;
MockHttpServletRequest req;
MockHttpServletResponse res;
MockFilterChain filterChain;
@Before
public void setup() throws UnsupportedEncodingException {
this.subject = new HeaderWriterFilter(Arrays.<HeaderWriter>asList(this.writer));
this.req = new MockHttpServletRequest();
this.res = new MockHttpServletResponse();
this.filterChain = new MockFilterChain() {
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
response.getWriter().write("lets flush the stream");
// the HeaderWriter has not been invoked yet
verifyZeroInteractions(
HeaderWriterFilterNotWrittingHeadersTest.this.writer);
// as soon as we invoke flushBuffer it should invoke the writer
response.flushBuffer();
// verify the writer has been invoked
verify(HeaderWriterFilterNotWrittingHeadersTest.this.writer).writeHeaders(
any(HttpServletRequest.class), any(HttpServletResponse.class));
// invoke the super class to indicate all the verifications have been invoked
super.doFilter(request, response);
}
};
}
@Test
public void testWritingHeaders() throws ServletException, IOException {
this.subject.doFilter(this.req, this.res, this.filterChain);
// verify that the MockFilterChain was invoked along w/ its assertions
assertThat(this.filterChain.getRequest()).isNotNull();
}
} |
Moving this to the Backlog until someone can reproduce it. |
I also reproduced the issue on Tomcat/JBoss when the dispatcher forwards to a JSP |
Thanks for taking care of this issue! |
@rwinch This issue should be re-opened. I believe it has manifested itself again since the commit that closed this issue (57d7ad0) was reverted (ea3dd33). I can set a breakpoint in Now this seems to be related to the size of the file that I am returning in the response body. I noticed that a 122544 byte file did not have the headers set and a 15385 byte file did. I believe this behaviour comes down to the logic in
Its entire functionality seems to be based on the premise that as long as we write less than However I can see that my Jetty
It seems that Jetty is committing the response after 8192 bytes are written and hence sometimes the headers are not being written. edit Javadoc from
Versions
|
I am experience this when using Springboot 2.x (embedded tomcat) + Spring security 5.x + Zuul proxy. The header writer gets triggered when org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter writes the response and flushes it, which triggers the: org.springframework.security.web.header.HeaderWriterFilter.HeaderWriterResponse but if the response from downstream service is smaller than the (tomcat) response buffer size, the response is committed and the writing headers (adding header to already committed response) is skipped. |
@sandipchitale Thanks for taking the time to provide feedback. Can you please create a new issue rather than commenting on a closed ticket. This will help us ensure your feedback does not get lost. On the new issue please provide a link to this issue and details on how to reproduce the problem (ideally a sample or test that demonstrates the issue). |
Summary
Spring Security 4.1.0.RELEASE and 4.1.1.RELEASE: Spring Security does not write HTTP response headers in the following scenario.
Actual Behavior
Starting from Spring Security 4.1.0.RELEASE, the method HeaderWriterFilter#doFilterInternal first does the chain through the filterChain#doFilter method giving it the possibility to flush the response and finally it tries to write the headers. However if the above mentioned happens, the headers will not be sent to the client as the underlying http outputstream was already flushed.
Here's the code:
In the 4.0.1.RELEASE version, it happens the other way (so headers are effectively written and received by the clients) (assuming this filter was the first one in the chain):
So in previous versions, the headers are tried to be sent to the client BEFORE the remaining participants of the filterChain could attempt to put data in the stream, however in the new version it can happens (for example) that a JSP view flushes the buffer BEFORE the finally block of the doFilterInternal method is reached
So this is what is happening to summarize:
Expected Behavior
The headers should be written ALWAYS and there should be no way for any other participant in the filterChain to prevent this.
Configuration
Version
4.1.0.RELEASE, 4.1.1.RELASE
Related
#2953
Sample
The text was updated successfully, but these errors were encountered: