Как отключить флаг HttpOnly при установке заголовка cookie-файла при входе в Spring Boot 2.1.0

#spring #spring-boot #tomcat #spring-security #tomcat8

#spring #spring-boot #tomcat #spring-безопасность #tomcat8

Вопрос:

У меня возникли проблемы с отключением флага HttpOnly в заголовке set-cookie. В основном это проблема при входе в систему, когда JSESSIONID отправляется обратно в ответе. Обратите внимание, что это на сервере tomcat, развернутом на AWS EBS.

Любой из приведенных ниже конфигураций отлично работает локально, но не при развертывании.

Я пробовал следующие решения, похоже, ни одно из них не работает

приложение.конфигурация yml

 server:  
  servlet:
    session:
      cookie:
        http-only: false
  

Инициализатор контекста сервлета

 @Bean
open fun servletContextInitializer(): ServletContextInitializer {
    return ServletContextInitializer { servletContext ->
        servletContext.setSessionTrackingModes(setOf(SessionTrackingMode.COOKIE))
        val sessionCookieConfig = servletContext.sessionCookieConfig
        sessionCookieConfig.isHttpOnly = false
    }
  

WebServerFactoryCustomizer

 @Bean
open fun tomcatCustomizer(): WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
    return WebServerFactoryCustomizer { tomcat ->
        tomcat
            .addContextCustomizers(TomcatContextCustomizer { context -> context.useHttpOnly = false })
    }
  

web.xml

     <session-config>
      <cookie-config>
        <http-only>false</http-only>
      </cookie-config>
    </session-config>
  

Пример заголовка запроса

 Host: 
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: application/json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: 
Authorization: Bearer null
Content-Type: application/json
Content-Length: 58
Origin: 
Connection: keep-alive
TE: Trailers
  

Пример заголовка ответа

 HTTP/2.0 200 OK
date: Sat, 16 Mar 2019 14:11:58 GMT
set-cookie: AWSALB=qBpX9uFjtkP4H7gyJ3EXL8na0a7aARiEN/twi0cc2sPywvbysKXXaNfQbe8HaS5hcC6VRnkp09VYj0pGcXiHbWRod9OithDlQ0ZIvHSbY7B5xiJT1r8N lcRdCcp; Expires=Sat, 23 Mar 2019 14:11:57 GMT; Path=/
server: Apache/2.4.37 (Amazon) OpenSSL/1.0.2k-fips
vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers
access-control-allow-origin: 
access-control-allow-credentials: true
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
strict-transport-security: max-age=31536000 ; includeSubDomains
x-frame-options: DENY
set-cookie: JSESSIONID=70F12355ABFDD0F42292D9F6CEAA22BF; Path=/; Secure; HttpOnly
X-Firefox-Spdy: h2
  

Комментарии:

1. Как вы развертываете свое приложение на AWS, в виде jar или war внутри tomcat ?

2. файл war с использованием EBS deploy

3. С помощью балансировщика нагрузки. К сожалению, балансировщик нагрузки — это не то, что я могу отключить / обойти. Повлияет ли балансировщик нагрузки на набор cookie JSESSIONID?

4. @dur отлично работает на локальном сервере tomcat. Может быть какая-то конфигурация в AWS Application Load Balancer? Следует отметить одну вещь, SSL завершается в подсистеме балансировки нагрузки. Соединение между балансировщиком нагрузки и экземпляром осуществляется только по протоколу HTTP. Тем не менее, флаг Secure добавляется к установленному файлу cookie JSESSIONID

5. @dur да, вы правы. Однако флаг Secure поднимает вопрос о том, могут ли у балансировщика нагрузки быть какие-то правила

Ответ №1:

Я, наконец, смог решить эту проблему, создав фильтр, который выполняется как часть Spring Security. Фильтр выполняется перед SecurityContextPersistenceFilter, таким образом, ожидая, пока не будет добавлен заголовок set-cookie, затем обновляет заголовки (перед тем, как в цепочке выполняется последний вызов после выполнения doFilter()).

Реализация фильтра

 package com.zambezii.app.security.filter

import org.springframework.web.filter.GenericFilterBean
import java.io.IOException
import javax.servlet.FilterChain
import javax.servlet.ServletException
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

class SessionFilter : GenericFilterBean() {

    @Throws(IOException::class, ServletException::class)
    override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
        val req = request as HttpServletRequest
        val res = response as HttpServletResponse
        chain.doFilter(req, res)

        removeHttpOnlyFlag(res)
    }

    private fun removeHttpOnlyFlag(res: HttpServletResponse) {
        val setCookieHeaderName = "set-cookie"
        var setCookieHeader = res.getHeader(setCookieHeaderName)

        if (setCookieHeader != null) {
            setCookieHeader = setCookieHeader.replace("; HttpOnly", "")
            res.setHeader(setCookieHeaderName, setCookieHeader)
        }
    }
}
  

Конфигурация безопасности

 open class WebSecurityConfig() : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
            ...
            .authenticated()
            .and()
            .addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter::class.java)
            .addFilterBefore(SessionFilter(), SecurityContextPersistenceFilter::class.java)