본문 바로가기
Http

Cookie SameSite 기본편

by ybs 2021. 2. 5.
반응형

쿠키는 stateless(상태 정보를 유지하지 않는) HTTP protocol 에서 stateful 하게 사용할 수 있게 해주는 수단이다.

먼저 쿠키의 기본 성질에 대해서 알아보자.

 

1. 서버에서 응답 헤더로 아래와 같은 Set-Cookie 헤더를 보내주면 이후 요청마다 브라우저가 헤더에 쿠키 정보를 같이 전송해준다.

 

 

2. admin.ybs.com 도메인으로 쿠키를 만들었을 때, 브라우저는 하위 도메인 요청에도 쿠키를 같이 전달한다.

 

SameSite

SameSite 탄생 배경

Set-Cookie: ybs=1234; Path=/; Domain= admin.ybs.com; SameSite=Strict

위와 같이 쿠키에 SameSite 속성이 새롭게 추가 되었다.

 

사실 새롭다고 하기는 뭐한게 2016년에 draft-west-first-party-cookies-05 문서에서 처음으로 등장했다.
이후 RFC 6265 draft bis 02(2017-08-07 ~ 2018-02-08) 에서 추가되었고 03(2019-04-27 ~ 2019-10-29) 를 거쳐
2021년 2월 기준 07 까지 나왔다(tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-07).

 

그런데 http 쿠키 스펙을 찾다보면 tools.ietf.org/html/draft-west-cookie-incrementalism-01 문서에 대해서도 나오는데 두 문서의 관계는 무엇일까? 그에 대한 답은 github.com/httpwg/http-extensions/issues/1241 에서 확인할 수 있다. draft-west-cookie-incrementalism 문서는 구글이 RFC6265 draft bis 수정을 제안하는 목적이라고 한다.

결국 구글 크롬은 이 draft 버전에 맞춰 업데이트를 진행 하고, 정식 RFC 문서가 아님에도 실질적으로 영향력이 크다.

 

 

SameSite 가 나온 배경을 살펴보면 RFC 문서에 다음과 같은 문장이 있다.

"SameSite" cookies offer a robust defense against CSRF attack when deployed in strict mode, and when supported by the client.

"SameSite" 쿠키는 strict 모드 일 때, 그리고 클라이언트 지원을 받을 때 CSRF(Cross-site request forgery) 공격에 대해 강력한 방어 기능을 제공한다.

CSRF 공격을 막기 위한 보안 목적으로 SameSite 속성이 생겼고, 기존에 없던 새로운 제약이 생길거라는것을 알 수 있다. 먼저 SameSite 속성에 대해서 자세히 살펴보고 그후에 어떻게 CSRF 공격을 막을 수 있는지 설명하겠다.

SameSite 기준 및 범위

Set-Cookie: ybs=1234; Path=/; Domain= admin.ybs.com; SameSite=Strict
admin.ybs.com 도메인과 SameSite 가 아닌 경우, 브라우저에서 쿠키 생성과 전달을 제한한다는 뜻이다.

좀 더 자세한 설명을 위해 아래 두 케이스를 보자. 서버에서 응답으로 전달된 Set-Cookie 헤더의 Domain 값과 웹 페이지 주소창에 있는 도메인이 같은게 있고 다른게 있다. 위의 1번은 두 도메인이 같고 2,3번은 다르다. 하지만 모두 브라우저가 쿠키를 저장할 수 있고 전달이 가능하다.

하지만 밑의 경우에는 SameSite=Strict 속성이 추가됐다. 이럴 경우 어떻게 될까? 1번은 도메인이 서로 같으니 SameSite 같다. 2번, 3번은 도메인이 달라서 딱봐도 SameSite 가 아닌거 같다. 그런데 4번은 직관적으로 바로 판단이 안선다.


결국 SameSite 판단 기준을 정확히 아는게 중요하다(5.2. "Same-site" and "cross-site" Requests).

 

A request is "same-site" if its target's URI's origin's registrable domain is an exact match for the request's client's "site for cookies",
or if the request has no client. The request is otherwise "cross-site".

위 문장은 SameSite 판단 기준에 대한 RFC 문서 내용이다(해당 내용은 draft-ietf-httpbis-rfc6265bis-07 에서 바뀌었는데 아래에 따로 정리했다).

 

target's URI's origin's registrable domain 과 request's client's "site for cookies" 가 정확히 일치하거나 request 가 client 를 갖지 않을 때 SameSite 라고 한다. 이것만 봐서는 이해하기 힘들다. 각 워딩 개념을 정리하고 다시 보도록 하자.

 

1) TLD(top level domain)
TLD + 0 레벨(public suffix)에 해당하는 도메인들은 https://publicsuffix.org/list/public_suffix_list.dat 에서 관리하는데
예를 들어 "com", "github.io", "co.kr” 이 있다.
TLD + 0 레벨은 마지막 dot(.) 기준 오른쪽 문자열이 아니다.

 

2) registrable domain 은 TLD + 1 레벨의 도메인이다(중요).
ex) https://www.example.com 에서 public suffix 은 "com", registrable domain 은 "example.com"

 

3) 브라우저 주소창 URI's origin 의 registrable domain 을 top-level site 라고 한다.

위와 같이 top-level browsing context 라면(iframe 같이 중첩된 브라우저가 아닌) "site for cookies" 는 top-level site 이다.

cf) site for cookies 는 RFC 에서 나온 워딩이다. 왜 site for cookies 가 top-level site 인지 추가적인 설명은 없다.

 

nested-browsing context 라면 각 document 의 상위 browsing context 의 origin 을 확인해야 한다.
하위 document 와 상위 document 의 origin 이 같은 registrable domain 일 때만 "site for cookies" 는 top-level site 이다.
즉, request's client's "site for cookies"는 브라우저 주소창 URI 의 registrable domain 이다.

 

결론적으로 Set-Cookie Domain 의 registrable domain 과 브라우저 주소창 URI 의 registrable domain 이 정확하게 일치할 경우 SameSite 이다.

 

아래의 그림을 보면서 다시 정리해보자.

브라우저 주소창 URI 의 registrable domain 은 ybs.com(TLD+1) 이다.
Set-Cookie Domain 의 registrable domain 도 ybs.com(TLD+1) 이다. 그러므로 SameSite 다.

 

그리고 아까 RFC 문서에서 request 가 client 를 갖지 않을 때도 SameSite 라고 했는데, 이는 브라우저 주소 창에서 직접 타이핑 쳐서 html 이 바뀌는 경우를 말한다. 왜 그렇게 되는지는 아래에 SameSite 인지 CrossSite 인지 판단하는 알고리즘에서 답을 찾을 수 있다.

브라우저 주소 창에서 직접 타이핑 쳐서 html 이 바뀔 때 request 의 client 가 null 이 되고 SameSite 로 리턴되는것을 알 수 있다.

For a given request ("request"), the following algorithm returns "same-site" or "cross-site":
1. If "request"'s client is "null", return "same-site".
Note that this is the case for navigation triggered by the user directly (e.g. by typing directly into a user agent's address bar).
2. Let "site" be "request"'s client's "site for cookies" (as defined in the following sections).
3. Let "target" be the registrable domain of "request"'s current url.
4. If "site" is an exact match for "target", return "same-site".
5. Return "cross-site".

 

Schemeful SameSite 

draft 07버전부터 scheme(http, https) 까지 같아야 SameSite 인것으로 바뀌었다. SameSite 기준이 좀 더 엄격해졌다.

아래 스펙 알고리즘을 보면 A, B 두개의 scheme이 같지 않으면 false 를 리턴하는것을 알 수 있다.

Two origins, A and B, are considered same-site if the following
   algorithm returns true:

   1.  If A and B are both the same globally unique identifier, return
       true.

   2.  If A and B are both scheme/host/port triples:

       1.  If A's scheme does not equal B's scheme, return false.

       2.  Let hostA be A's host, and hostB be B's host.

       3.  If hostA equals hostB and hostA's registrable domain is null,
           return true.

       4.  If hostA's registrable domain equals hostB's registrable
           domain and is non-null, return true.

   3.  Return false.

   Note: The port component of the origins is not considered.

   A request is "same-site" if its target's URI's origin is same-site
   with the request's client's "site for cookies" (which is an origin),
   or if the request has no client.  The request is otherwise "cross-
   site".

 

예를 들어 아래 그림에서, 06 버전 까지는 SameSite 고 정상적으로 브라우저에서 set-cookie 가 가능했다.

하지만 07버전 부터는 scheme이 달라 CrossSite 가 된다.

 

SameSite 옵션

옵션은 총 3가지가 있다.

 

1) SameSite=Strict

모든 cross-site subresource requests, cross-site nested navigations 상황에서 쿠키 생성 및 전달을 허용하지 않는다.
여기서 subresource requests 는 html 파일 안에서 js, css, img 등의 리소스 가져올 때를 말한다. navigation 개념도 중요한데 이는 다른 옵션에서 자세하게 설명하겠다.

 

2) SameSite=None
반드시 secure 속성이 함께 추가되어야 동작한다. 즉 HTTPS 만 가능하다.

3) SameSite=Lax
top-level navigations(which use a safe HTTP method) 일 경우에만 cross-site 상황에서 쿠키 생성 및 전달을 허용한다.

먼저 safe HTTP method 는 "GET", "HEAD", "OPTIONS", and "TRACE" 이다(RFC7231).

 

navigation 개념

navigation 은 서버에서 html 을 보내줘서 페이지 이동이 되는 케이스를 말한다.
웹페이지에서 링크를 눌렀을 때 보통 2가지 flow 가 생긴다.
첫째, 서버에서 html 을 보내줄 때
둘째, 서버에서 다운로드 파일을 보내줄 때

이 중 첫번째에 해당하는게 navigation 이다.

 

top-level navigation 은 main frame 이 이동하는 케이스를 말한다 .
iframe 이 있는 페이지 경우, iframe 내부 이동은 top-level navigation 이 아니기 때문에 구분 짓기 위한 개념이다.

 

예를 들어 메일을 읽고 있는데 내용에 다른 링크 주소가 있다고 하자.

만약 projects.example 이 SameSite 이면 cross-site navigation 이기 때문에 인증에 실패할 수 있다.

그럴 경우 오동작하는 사이트들이 많아서 top-level navigations(safe HTTP method) 에 한해 third-party 쿠키를 허용해주는 Lax 모드를 지원하는 것이다.

 

4) Set-Cookie 에 SameSite 속성이 없는 경우
Incrementally Better Cookies draft-west-cookie-incrementalism-00(2019-05-07 ~ 2019-11-08) 에서는 SameSite=Lax 로 한다고 되어 있다.

그리고 chromium FAQ 에서도 SameSite=Lax 로 한다고 되어 있다.

 

5) Set-Cookie 에 SameSite=Strict; SameSite=Lax; SameSite=None 여러개 붙은 경우
마지막 SameSite=None 을 선택한다.

 

Spring 및 톰캣 SameSite 속성 지원

Spring Framework
spring-web module 5.1 버전 Spring WebFlux 에서 ResponseCookie 클래스에 SameSite 속성이 추가되었다.

 

Tomcat
2019-06-07 release 된 Tomcat 9.0.21 버전과 Tomcat 8.5.42 버전에서 context.xml 에 sameSiteCookies 속성을 추가하여
일괄적으로 SameSite 속성을 set-Cookie 헤더에 추가할 수 있다(Tomcat 7.x 버전은 지원하지 않음).

<Context>
<CookieProcessor sameSiteCookies="strict" />
</Context>

 

CSRF

위 그림에서 CSRF 공격은 ybs.com 에 대한 인증을 받은 상태(브라우저에 ybs.com 도메인 인증 쿠키가 존재하는)라는 전제가 있을 때 가능하다. 하지만 만약 쿠키 SameSite 옵션이 Strict 라면, CrossSite 일 때 쿠키 전달을 차단 할 수 있다.

 

참고로 알아두면 좋은 정보

1) RFC 문서 볼 때 동사 주의
Must, Shall, Required : 반드시 지켜야 하는 스펙
Should : 정말 타당한 이유가 있을 때 안지켜도 되지만 신중하고 정확히 이해되야 함
May : 필수가 아님, 구현 안해도 되지만 구현한거랑 안한거랑 상호운용이 가능해야함

2) 일부 user-agent 에서는 Domain 속성이 없을때 현재 Host 로 쿠키를 저장(RFC6265)

하지만 Host 로 쿠키를 생성하면, admin.ybs.com 도메인 한정이다.
즉, test.admin.ybs.com 같은 서브 도메인에는 쿠키가 전달 되지 않는다.

 

 

참고 : tools.ietf.org/rfcdiff?url2=draft-ietf-httpbis-rfc6265bis-07.txt
참고 : https://github.com/spring-projects/spring-framework/wiki/What's-New-in-Spring-Framework-5.x#spring-webflux-1
참고 : https://github.com/spring-projects/spring-framework/issues/23693

참고 : https://github.com/apache/tomcat/pull/162 
참고 : https://github.com/apache/tomcat/pull/219 

참고 : https://tools.ietf.org/html/rfc2119

 

SameSite cookie attribute missing when using WebFlux · Issue #23693 · spring-projects/spring-framework

As of Spring Framework 5.1 the "What’s New” wiki documentation states the following for Spring WebFlux (https://github.com/spring-projects/spring-framework/wiki/What%27s-New-in-Spring-Framewor...

github.com

 

 

반응형