Cookie and SameSite

올 2월부터 Chrome 브라우저에서 SameSite=Lax가 기본값으로 변경됩니다.

  • Early October, 2019: Experimental SameSite-by-default and SameSite=None-requires-Secure behavior launched to 50% of users on Chrome Canary and Dev (Chrome Canary and Dev versions 78+). Windows and Mac users on domain-joined devices and Chrome OS users on enterprise-registered devices will be excluded from the experiment. Chrome 78 Beta users will not receive the experimental behavior. October 31, 2019: Chrome 79 Beta released. Experiment extended to 50% of Chrome 79 Beta users, including domain-joined and enterprise-registered devices. Policies to manage the experimental behavior (see below) will be available on Chrome 79.
  • Dec 10, 2019: Chrome 79 Stable released. Stable users on Chrome 79 will NOT receive the new SameSite behavior.
  • Dec 19, 2019: Chrome 80 Beta released. Experimental behavior still enabled for 50% of Chrome 80 Beta users.
  • February, 2020: Chrome 80 Stable will begin rolling out over a period of time. SameSite-by-default and SameSite=None-requires-Secure will then start being enabled as the default behavior during the Chrome 80 Stable lifecycle.

그리고 SameSite=None을 사용하기 위해선 해당 쿠키에 Secure가 강제된다고 하니.. 꼭 참고하시길 바래요.

참고로 현재 Chrome에서는 2분 이내 SameSite 속성 없이 설정된 쿠키에 대해서는 예외를 가지고 있습니다. 예외에 해당되는 쿠키는 Lax 정책을 가지지만 POST 등 일부 메소드의 Cross-site Request에서도 쿠키가 포함되어 전달됩니다. 추후에는 이 예외가 제거될 예정이라고 하네요.

보안쪽 입장에선 환영할 부분이지만, 버그바운티헌터나 개발자 입장에선 고려해야할 부분이 많아지는건 사실입니다. 작년에 가볍게 보고 넘어갔던 내용인데, 최근에 리서치해보고 테스트해볼일이 있었는데, 복습 차원으로 글로 남겨봅니다.

쿠키와 도메인의 관계

기본적으로 쿠키는 대상 도메인의 기준으로 전송 유무가 판단됩니다. 예를들어 www.google.com에 발급된 쿠키는 www.hahwul.com에선 사용할 수 없지만 www.hahwul.com에서 www.google.com으로 요청이 발생하거나 페이지가 이동할 땐 쿠키가 붙어서 전송됩니다. 아래처럼 gmail.com이 <a> 태그로 있는 경우도 동일하죠. (구글 로그인이 되었다면 gmail이 열리겠죠.)

<a href="https://mail.google.com">Gmail</a>

물론 이 과정은 쿠키의 설정에 따라서 달라질 수 있습니다. 쿠키는 발급 시 사용할 수 있는 domain, path 등을 명시할 수 있고 조금 더 강력한 정책으로 SameSite 설정을 통해 어떤 도메인에 쿠키를 허용할지 쿠키 발급자가 정해줄 수 있습니다. 아래에서 설명하겠지만, 이번에 Default 값으로 변경되는 Lax의 경우 무조건 SameSite 인지 체크하고, 허용된 몇개의 패턴 이외에는 SameSite가 아니면 쿠키를 전송하지 않도록 강제하는 쿠키 정책입니다.

https://www.chromium.org/updates/same-site

방금까지 이야기한 내용만 토대로 보면 CSRF의 대응방안이 될 수 있습니다. LaxStrict가 걸려있는 한 공격자가 CSRF를 성공하기 위해선 Cross-domain 정책을 우회할만한 환경이나 기술이 더 필요해진거죠. 관련해선 임준오님 블로그 글 한번 읽어보시면 좋습니다.

SameSite Policy

SameSite 정책은 크게 3가지(None, Lax, Strict)로 나뉘어 있습니다. 각각 내용을 살펴볼게요.

SameSite=None

None은 현재까지 사용되던 Default 정책으로 SameSite를 검증하지 않습니다. 그래서 A 사이트에서 B 사이트로 요청을 전송하게 되면 B 사이트의 쿠키가 붙어서 전송됩니다.

SameSite=Strict

Strict는 쿠키의 SameSite 검사를 강하게 제한하는 정책으로 소스가 되는 도메인과 대상 도메인이 일치해야만 쿠키가 포함되어 전송됩니다. 예를들면..

(O) www.google.com => www.google.com
(X) www.hahwul.com => www.google.com

여기에 영향받는 것들은 <img> <form> <iframe> $.get() 등 모든 요청을 전송할 수 있는 것들을 의미합니다.

SameSite=Lax

마지막으로 Lax는 기존 Strict 정책에서 예외처리가 몇개 된 정책이라고 생각하시면 됩니다. 일반적으로 GET을 사용하는 요청 중 앵커태그(<a href>) , form의 get 메소드(<form method=get>) 정도만 예외되고 나머지는 Strict와 동일하게 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.

다만 Lax에서 허가된 것들이 “Safe”한 HTTP Method 이고, 이건 GET을 의미하는 것 같은데요.. 많은 경우가 있듯이 POST에서 GET으로 변경 했을 때 가능한 CSRF도 굉장히 많기 때문에 이 또한 인지하고 테스트해야할 부분인 것 같네요. 간단하게 Cross-site 환경에서 Iframe으로 테스트해보면 이렇습니다.

*SameSite=None 일 때 `<iframe src=//www.hahwul.com>` 에서 발생한 요청*

SameSite=Lax 일 때 `<iframe src=//www.hahwul.com>` 에서 발생한 요청. Cookie 발생하지 않음

SameSite의 정확한 기준

작년에 SameSite에 대해 처음 알았을땐 사실 도메인에 대한 기준에 별 관심이 없어서 그냥 지나쳤었는데, 최근에 이와 관련해서 테스트해보고 여러 의견을 나눠본 결과.. 매우 중요했단 사실을 알게 됬습니다.

요점은 www.google.comaaa.google.com, 즉 서브 도메인만 다른 경우 SameSite인가? 이고 결론은 SameSite 입니다. 다만 하나 알고가야할건 Public suffix에 명시된 최상위 도메인을 기준으로 SameSite를 식별한다는 점 입니다. 그래서, 1.google.com2.google.com은 SameSite이지만, 1.github.io2.github.io는 Cross-site 입니다.

이에 대한 자세한 내용은 web.dev쪽 링크 한번 읽어보시면 좋습니다 :D

Key Term: If the user is on www.web.dev and requests an image from static.web.dev then that is a same-site request. The public suffix list defines this, so it’s not just top-level domains like .com but also includes services like github.io. That enables your-project.github.io and my-project.github.io to count as separate sites.

이 개념을 이해할 땐 2가지를 알고 있는게 좋습니다. 바로 TLD와 eTLD입니다.

TLDs and Site

TLDs는 Top-Level Domain으로 최상위 도메인을 의미합니다. 보통 많이 사용되는 .com, .net 이 바로 TLD입니다. 그리고 TLD와 바로 앞의 도메인의 조합을 Site라고 부릅니다.

  • TLD: .com
  • Site(TLD+1): hahwul.com

그래서 SameSite의 규칙은 이 Site를 기준으로 동작하게 됩니다. 그래서 www.google.comaaa.google.com 은 같은 Site가 됩니다.

eTLDs

제가 Public suffix에 대한 이야기를 했는데, 이는 바로 eTLDs(effective TLDs)가 Site의 기준에 포함되기 떄문입니다. 그럼 eTLD는 어떤걸까요? github.io 같이 Site(Domain + TLD)로 Site를 식별하기에는 여럿이 공유하게 되는 도메인이나 세분화하기 어려운 경우 Public suffix에 eTLDs로 정의해두고 마치 github.io 가 Site가 아닌 TLD처럼 동작하게 할 수 있습니다.

  • eTLD: github.io
  • Site(eTLD+1): hahwul.github.io

Site

그래서 실제로 Site는 Domain+TLD (TLD+1) 또는 Domain+eTLD (eTLD+1)로 구성됩니다. 웹에서 동작하는 Site 기반의 규칙은 이 부분을 적용받게 됩니다.

PublicSuffix

공식 Repo와 주소는 아래와 같습니다. 만약 도메인 소유주가 eTLD로 등록하고 싶다면 아래 Repo를 Fork 후 public_suffix_list.dat 파일을 수정한 후 PR하여 신규로 생성 요청할 수 있습니다.

크롬만 SameSite=Lax를 적용하는건가?

크롬이 2월에 예정되어 있어서 보안이나 개발 모두 신경쓰고 있어야하는 부분입니다. 그럼 다른 브라우저는 어떻게 되는걸까요? 우선은 SameSite 는 크롬보다 Firefox나 Safari의 구현이 더 빨랐고 현재도 대다수 브라우저들이 모두 지원하는 상태입니다. 아직은 확실하게 날짜가 나오진 않았지만, Firefox를 비롯해서 다른 브라우저도 Default 값으로 가져가게 되는 것 같습니다.

이는 각 브라우저사가 서로의 정책을 공유하고 표준처럼 구성하려는 Consensus & Standardization에 의해서 아마 특별한 일이 없다면 Safari, Firefox 모두 비슷한 시기에 동일한 정책을 적용할 가능성이 높습니다.

How to Testing and Programming?

자 이제 어떤건지 알아봤으니 테스트하는 방법을 간략하게 정리해봅시다. 각 브라우저로 별로 현재(20년 1월)까진 기본값이 None으로 설정된 상태라서 테스트가 어렵습니다. 크게 2가지 방법 정도로 테스트해 볼 수 있는데요.

Browser Config

가장 좋은 방법입니다. 크롬(76 이상)과 파폭(69 이상) 모두 Config를 통해 강제로 Lax를 Default로 동작시킬 수 있습니다.

1) FireFox Firefox의 경우 about:config 진입 후 network.cookie.sameSite.laxByDefault 를 true로 바꿔쥐면 Default가 Lax로 동작합니다.

2) Chrome Chrome 또한 flags에서 Enable 시켜주시면 Default가 Lax로 동작합니다. chrome://flags/#cookies-without-same-site-must-be-secure

두번째는 EditThisCookie 같은 Addon을 통한 변경 방법입니다. 위 기본값을 바꾸는 것과는 다르게 특정 쿠키만 Lax, Strict를 줄 수 있기 때문에 쿠키 정책을 테스트해볼때 제일 적합한 도구라고 보이네요.

SameSite 구현 예시

물론 전 개발자가 아니기 때문에 구현해야할 일이 많지는 않겠지만, 크롬쪽에서 예시 페이지를 공유해주고 있어서 같이 포함해봤습니다. https://github.com/GoogleChromeLabs/samesite-examples

뭐 사실 별거 없습니다… 그냥 Set-Cookie 시, 또는 JS 내부에서 세팅 등 쿠키를 설정할 때 SameSite 속성에 값을 할당해주면 됩니다.

document.cookie = 'same-site-cookie=foo; SameSite=Lax';
document.cookie = 'cross-site-cookie=bar; SameSite=None; Secure';

워낙 잘 나와있어서 보고 적용해보시면 될 것 같습니다 :)

References