본문으로 바로가기



20년 2월 4일 릴리즈된 구글 크롬(Google Chrome)80버전부터 새로운 쿠키 정책이 적용 되어 Cookie의  SameSite 속성의 기본값이 "None"에서 "Lax"로 변경되었습니다.

SameSite 를 None 으로 설정할 경우 모든 도메인에서 쿠키를 전송하고 사용할 수 있지만  사용자가 사이트 간 요청 위조(CSRF - Cross-site request forgery) 및 의도하지 않은 정보 유출에 취약해질 가능성이 있습니다. 이러한 취약점을 방지하기 위해 지금까지는 별도의 SameSite 속성 명시 없이 쿠키를 생성했을 때 "SameSite=None" 으로 설정한 것과 동일하게 동작 했지만 Chrome80 버전 이후에는 SameSite 속성 설정이 없는 쿠키는 "SameSite=Lax" 로 명시한 것과 동일하게 동작한다는 것입니다.


기본 보안 설정이 올라 감에 따라 결제(PG)나 인증 모듈 등의 써드파티 시스템과 연계된 서비스가 정상적으로 동작하지 않을  수 있어 급히 관련 내용을 찾아보신 분들도 많으실 거라 생각됩니다. 사실 개발자분들이 쿠키를 생성할때 특수한 경우가 아니면 SameSite속성을 명시적으로 설정하는 경우가 많지 않죠. 많이 사용하는 Java의 Cookie (javax.servlet.http.Cookie) Class에서는 SameSite 관련 API를 지원하지도 않아 해당속성을 모르시는 분들도 있으셨을 것 같습니다. 

이후 Firefox, Edge 등 다른 브라우저도 Chrome 과 동일한 설정으로 기본값을 변경한다고 하니 이참에 내용을 알아두시면 좋을 것 같네요.

| SameSite 

Cookie의 SameSite 속성은 서로 다른 도메인간의 쿠키 전송에 대한 보안을 설정합니다.
A cookie with "SameSite=Strict" will only be sent with a same-site request.
A cookie with "SameSite=Lax" will be sent with a same-site request, or a cross-site top-level navigation with a "safe" HTTP method.
A cookie with "SameSite=None" will be sent with both same-site and cross-site requests.
위와 같이 "None"은 동일 사이트과 크로스 사이트에 모두 쿠키 전송이 가능합니다. 그리고 "Strict"로 설정할 경우 서로 다른 도메인에서는 아예 전송이 불가능해 지기 때문에 CSRF를 100% 방지할 수 있으나 사용자 편의성을 많이 해치게 됩니다. 그래서 Strict 설정에 일부  예외( HTTP get method / a href / link href )를 두어 적용되는 설정이 이번에 기본값으로 변경되는 "Lax입니다. 

브라우저에서 기본 설정을 변경한 것은 크로스 도메인간 중요한 정보 유지는 CSRF 가능성이 있는 쿠키가 아닌 다른 안전한 방식으로 하기를 권장하기 때문입니다. 제안하는 대로 Lax 설정에서도 문제 없게끔 쿠키에 대한 의존성을 낮추는 것이 권장 되지만 바로 수정개발이 힘든 경우는 쿠키의 SameSite설정을 기존의 기본값이었던 None으로 설정하여 임시로 해결 할 수 있습니다.

| SameSite 설정하기

SameSite 속성을 변경하는 방법은 쿠키 생성하는 시점부터 설정해 주거나 필터 등을 이용하여 기존 쿠키에 속성을 추가하는 방법이 있고 Apache 또는 Nginx 같은 HTTP / Proxy 서버를 사용중이라면 서버 설정을 통해 일괄로 변경하는 것도 가능합니다.

| 1. JavaScript
document.cookie = "safeCookie1=foo; SameSite=Lax"; 
document.cookie = "safeCookie2=foo";  
document.cookie = "crossCookie=bar; SameSite=None; Secure";
SameSite 속성을 명시한 safeCookie1와 명시하지 않은  safeCookie2는 크롬80 이상부터 동일하게 동작합니다. (Default Lax 적용)

주의하셔야 할 점은 SameSite 속성을  None으로 설정할 경우  Secure 속성을 함께 추가해 주셔야 한다는 점입니다. Secure 속성을 추가된 쿠키는 HTTPS 프로토콜에서만 전송이 가능하며 SameSite가 None이지만 Secure 속성이 없을 경우 브라우저에서는 아래의 경고메시지와 함께 해당 쿠키를 적용하지 않습니다.
This set-cookie had the "SameStie=None" attribute but did not have the "Secure" attribute. which is required in order to use "SameSite=None" 

| 2. Java Application

Java 에서 많이 사용하는 javax.servlet.http.Cookie Class에서는 SameSite 관련 API를 지원하지 않기때문에  SameSite 속성을 추가하려면 HttpServletResponse 의 response 객체에  Set-Cookie 헤더(Header)를 직접 추가해 주셔야 합니다.
response.setHeader("Set-Cookie", "Test1=TestCookieValue1;   Secure; SameSite=None");
response.addHeader("Set-Cookie", "Test2=TestCookieValue2;  Secure;  SameSite=None");
response.addHeader("Set-Cookie", "Test3=TestCookieValue3;   Secure; SameSite=None");

주의 하셔야 할점은 setHeader 를 통해 Set-Cookie 헤더를 처음 추가한 뒤에 setHeader를 다시 사용할 경우 동일이름의 헤더가 오버라이드 되기 때문에 첫 추가 이후에는 addHeader를 통해 추가 해주셔야 합니다. 

만약 Spring Core 5.0 이상 버전을 사용중이시라면  org.springframework.http.ResponseCookie Class의 API 를 사용하여 조금 더 쉽게 처리가 가능합니다.
ResponseCookie cookie = ResponseCookie.from("sameSiteCookie", "sameSiteCookieValue")
            .domain("ifuwanna.tistory.com") 
            .sameSite("None")
            .secure(true)
            .path("/")
            .build();
response.addHeader('Set-Cookie', cookie.toString());

| 3. Filter or Interceptor 

위처럼 소스상에서 하나하나 처리가 힘든 경우 필터(Filter)나 인터셉터(Interceptor) 등을 이용하여 response 를 가로채 아래 로직을 통해 생성된 쿠키의 헤더에 Secure; SameSite=None 속성을 추가하여 일괄로 변경하는 것도 가능합니다.
public class CookieAttributeFilter implements Filter{
	
	
	@Override
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
		
		HttpServletResponse httpServletResponse = (HttpServletResponse) response;
		chain.doFilter(request, response);
		log.info("CookieAttributeFilter");		
		addSameSite(httpServletResponse , "None"); 
		
	}
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		// TODO Auto-generated method stub
	}
	@Override
	public void destroy() {
		// TODO Auto-generated method stub	
	}	
	
        private void addSameSite(HttpServletResponse response, String sameSite) {
    	
        Collection<String> headers = response.getHeaders(HttpHeaders.SET_COOKIE);
        boolean firstHeader = true;
        for (String header : headers) { // there can be multiple Set-Cookie attributes
            if (firstHeader) {
                response.setHeader(HttpHeaders.SET_COOKIE, String.format("%s; Secure; %s", header, "SameSite=" + sameSite));
                firstHeader = false;
                continue;
            }
            response.addHeader(HttpHeaders.SET_COOKIE, String.format("%s; Secure; %s", header, "SameSite=" + sameSite));
        }
        
    }
	
}

| 4. Tomcat 설정

Tomcat WAS 에서 지원하는 Cookie Processor Component 를 이용하여 일괄로 쿠키에 대한 속성을 추가 할 수 있습니다.
* context.xml
<Context>
    ...
    <CookieProcessor sameSiteCookies="none"/>
</Context>
Tomcat의 Cookie Processor Component 자세한 레퍼런스는 아래 document를 참고해 주세요!

| 5. WEB Server 설정 [ Proxy / HTTP Server ]

Apache 또는 Nginx 같은 HTTP 웹서버나 Proxy 서버를 사용중이라면 서버 설정을 통해 유저가 받는 모든 쿠키 속성을 한 번에 변경 할 수 있습니다.

* Apache Configuration
Header always edit Set-Cookie (.*) "$1; Secure SameSite=None;"
Nginx configuration
location / {
    # your usual config ...
    # hack, set all cookies to secure, httponly and samesite (strict or lax)
    proxy_cookie_path / "/; secure; SameSite=None";
}

Reference
https://github.com/GoogleChromeLabs/samesite-examples
https://tomcat.apache.org/tomcat-8.5-doc/config/cookie-processor.html



댓글을 달아 주세요

  1. smiley 2020.04.03 03:03

    그냥 궁금해서 그러는데 복사하기는 일부러 막아두신건가요?