[JAVA] Response header may not be output correctly in Spring Security 4.1

Introduction

As of September 22, 2017, the version of Spring Security is 4.2.3, so this issue does not occur: sweat :.

** 2018/9/12 postscript: ** ** In Spring Security 4.2.5, 5.0.2, the order of filter application → header writing is changed again, so the same problem occurs. In addition, there is a follow-up report in issue mentioned in the article, and it is flush that the behavior differs depending on the AP server. It looks like the difference between buffer size and commit size. ** **

However, it seems that Spring Security is shaking which specification is correct, so be careful when upgrading the version.

Confirmation environment

Spring Security
4.1.4.RELEASE (using TERASOLUNA Server Framework for Java (5.3.0.RELEASE))
AP server
JBoss EAP 7.0.0

To output the Cache-Control header with Spring Security in the first place

When outputting with TERASOLUNA, describe the headers element of Spring Security in spring-security.xml.

In the example below, the headers applied by default are disabled and only the components that output only the Cache-Control and X-Content-Type-Options headers are registered.

spring-security.xml


<sec:http>
	<sec:headers defaults-disabled="true">
		<sec:cache-control />
		<sec:content-type-options />
	</sec:headers>

Why is the response header not output only for 4.1?

Difference in header writing process procedure

The class that writes the header is ʻorg.springframework.security.web.header.HeaderWriterFilter, which is processed by the doFilterInternal` method, but the implementation has changed in Spring Security 4.1.0 RC1.

Related issue: SEC-2728: Only write cache related headers if no other cache headers found #2953

↓ 4.0.1. doFilterInternal method at RELEASE

HeaderWriterFilter.java


@Override
protected void doFilterInternal(HttpServletRequest request,
	HttpServletResponse response, FilterChain filterChain)
	throws ServletException, IOException {

	for (HeaderWriter factory : headerWriters) {
		factory.writeHeaders(request, response);
	}
	filterChain.doFilter(request, response);
}

↓ 4.1.0 doFilterInternal method at RC1

HeaderWriterFilter.java


	@Override
	protected void doFilterInternal(HttpServletRequest request,
			HttpServletResponse response, FilterChain filterChain)
					throws ServletException, IOException {

		HeaderWriterResponse headerWriterResponse = new HeaderWriterResponse(request,
				response, this.headerWriters);
		try {
			filterChain.doFilter(request, headerWriterResponse);
		}
		finally {
			headerWriterResponse.writeHeaders();
		}
	}

In 4.0.1, the processing is done in the order of header writing → filter application, but in 4.1.0, the order is filter application → header writing.

What's wrong?

By using try-finally, the header is written even if an exception occurs in the filter application process, but if the http stream is flushed in the filter application process, even if the header is written, it will be written to the client side. Will not be sent. (I couldn't figure out exactly what the problem was when I called the bug report issue: fearful :) The following are the target issues, so please refer to here for details.

Spring Security not writing http headers when the http stream was flushed by a participant of the filterChain. #3975

What kind of response do you need?

Let's upgrade the version of Spring Security: smiley: If you can't upgrade the version easily, replace HeaderWriterFilter. In the bug report issue of ↑, the change of HeaderWriterFilter is Revert, so HeaderWriterFilter other than Spring Security 4.1 It is OK if you use.

How to replace HeaderWriterFilter

If you place a HeaderWriterFilter other than Spring Security 4.1 in com.neriudon.sample.filter and name it CustomizedHeaderWriterFilter, it will look like this:

spring-security.xml


	<bean id="secureCacheControlHeadersWriter"
		class="org.springframework.security.web.header.writers.DelegatingRequestMatcherHeaderWriter">
		<constructor-arg>
			<bean
				class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
				<constructor-arg value="/**" />
			</bean>
		</constructor-arg>
		<constructor-arg>
			<bean
				class="org.springframework.security.web.header.writers.CacheControlHeadersWriter" />
		</constructor-arg>
	</bean>

	<bean id="customizedHeaderWriterFilter" class="com.neriudon.sample.filter.CustomizedHeaderWriterFilter">
		<constructor-arg ref="secureCacheControlHeadersWriter" />
	</bean>

	<sec:http>
		<sec:headers disabled="true" />
		<sec:custom-filter position="HEADERS_FILTER"
			ref="customizedHeaderWriterFilter" />

The point is

 <sec:headers disabled="true" />
        <sec:custom-filter position="HEADERS_FILTER"
            ref="customizedHeaderWriterFilter" />

It is the part of. You can use position to replace it with your own filter, but you have to setheaders disabled = "true"because it conflicts with existing filters. Therefore, the header to be output to the filter to be replaced is specified. This part is quoted from Output security header for each request pattern in TERASOLUNA guidelines. Did.

How I posted this article

When I raised the version of TERASOLUNA, the header was no longer output, so: frowning2: There is a comment that the behavior is different in Tomcat / JBoss even in the bug report issue, but ... It's deep: disappointed_relieved:

Recommended Posts

Response header may not be output correctly in Spring Security 4.1
Spring Security usage memo response header
Output request and response log in Spring Boot
[Resolved in 5.2.1] Spring Security 5.2.0.RELEASE has incompatibilities not mentioned in the release notes
JSESSIONID could not be assigned to the URL when using Spring Security
Output Spring Boot log in json format
To write Response data directly in Spring
File output bean as JSON in spring