HTTP-only Cookie 이슈
Spring Security로 사용자를 세션으로 인증하고 있었다. 그리고 주로 사용자 인증에 대한 로직은 로컬에서 확인이 됐기 때문에, 배포 환경에서 확인하진 못했었다. 그러다 세션이 유지가 되지 않는 현상(정확히 말하면, 쿠키로부터 세션 정보를 가져올 수 없음)이 발생했다. 낯설기도 하면서 처음 접한 내용이라, 이를 해결한 기록에 대해 남기려 한다.
이슈의 원인
위와 같이 HTTP-only Cookie를 사용했을 때, 서버로부터 전달받은 응답을 보면 Set-Cookie
에 JSESSIONID
값이 전달 된 것을 확인할 수 있다. 하지만 자세히 보면 HttpOnly ⚠️라고 warning 표시를 확인할 수 있다.
메시지를 자세히 보면 다음과 같다.
This Set-Cookie header didn't specify a "SameSite" attritubte and was defaulted to "SameSite=Lax," and was blocked because it came from a cross-site response which was not the response to a top-level navigation. The Set-Cookie had to have been set with "SameSite=None" to enable cross-site usage.
즉, SameSite
property를 설 정하지 않아, SameSite
가 기본값인 Lax
로 설정되어 cross-site response를 거부한다는 내용이다. 이를 허용해주려면 SameSite
attribute를 None
으로 하는 방법이 있다.
이때, origin에 대해 알고싶다면 Origin이란?을 참고하자.
참고로 서버가 클라이언트로 쿠키를 보낼 때는, Response Header의 Set-Cookie
필드에 담아 보내는데, 이에 대한 설명은 다음과 같다.
The Set-Cookie HTTP response header is used to send cookies from the server to the user agent.
해결 방법 1
해결 방법은 현재 두 가지를 확인할 수 있었는데, 첫 번째는 SameSite=None
으로 설정하는 것이다. 이는 모든 Context에서 Cookie가 전송되도록 명시하는 방식인데, 여러 사이트로부터 서비스를 제공하는 경우 사용할 수 있다.
Spring Boot를 사용한다고 가정한다면, application.yml
파일에서 다음과 같이 설정할 수 있다.
# application.yml
server:
port: 8080
servlet:
context-path: /v1
session:
cookie:
name: JSESSIONID
http-only: true # <------- added
same-site: none # <------- added
secure: true # <------- added
timeout: 10m
이때, 중요한건 same-site: none
옵션을 활성화 하려면, secure: true
보안옵션 또한 설정해줘야 하는 것인데, 이는 Intent to Deprecate and Remove: Insecure SameSite=None cookies에 잘 설명 돼 있다.
한 마디로 요약하면, HTTPS 사용을 권장하고, 이로써 일반 텍스트 채널에서의 쿠키의 전송을 보안전송으로 보호하려는 의도이다. 만약 secure: true
로 보안옵션이 활성화 되지 않았다면, 해당 쿠키는 reject 될 것이다.
이렇게 설정을 하면, Cross-origin에서도 서버에 요청을 하면 Http-only Cookie를 통해 세션이 유지될 수 있는데, 단, 클라이언트가 https scheme로 요청을 해야 한다.
해결방법 2
혹은, 특정 도메인만 활성화 해주는 경우가 있다. 이는 다음과 같이 설정할 수 있다.
# application.yml
server:
port: 8080
servlet:
context-path: /v1
session:
cookie:
name: JSESSIONID
http-only: true # <------- added
domain: thesurvey.kr # <------- added
timeout: 10m
구매한 도메인이 있다면 위와 같이 설정해 특정 도메인에서의 요청만 허용해줄 수도 있다. 그런데, google.com과 같이 공공 도메인(public suffix)을 사용할 경우 허용되지 않는다.