ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • SameSite 설정에 대해서 알아보자
    network, security 2022. 1. 24. 02:51

    브라우저 정책 변경의 습격을 받다

    대략 2020년 초 크롬이 Set-Cookie 헤더의 속성 중 하나인 SameSite의 디폴트 값을 변경하였다. 정확히는 SameSite=Lax로 변경하였는데(이 값의 의미는 아래에서 알아보도록 하자), 이 변경으로 인해 이전에는 쿠키가 잘 전송되던 기능에서 쿠키가 전송되지 않게 되었다. 이 이슈를 해결하려면 후술하겠지만 간단한 옵션 수정이 필요하다.

     

    추측하건대 파이어폭스는 크롬보다는 SameSite 정책 변경이 늦었던 것 같다. 내가 담당하는 어드민 기능이 하나 있는데, 예전부터 크롬으로는 아예 페이지 접근이 안 돼서 "왜 크롬으로는 안 될까?^^"라고 대수롭지 않게 생각하며 파이어폭스를 사용하고 있었다. 그런데 분명히 2022년 1월 초까지만 해도 파이어폭스에서 잘 접근되었는데, 갑자기 접근이 안 된다는 연락이 왔다. 사실 사파리의 쿠키 설정을 변경해서 해당 페이지에 접근할 수는 있었는데, 그건 근본적인 해결 방법도 아니고 mac OS를 사용하지 않는 직원들도 많기 때문에 이 문제를 더 이상은 무시할 수가 없었다. (여기에 따르면 파이어폭스 96버전부터 SameSite 디폴트가 변경된 것 같다. 시기가 2022년 1월 11일인 것보니 이 영향이 확실한 것 같다.)

     

    이 이슈를 해결하기 위해서 쿠키와 관련된 내용을 알아보다 보니 나의 무지와 게으름에 놀라지 않을 수가 없었다. 이 문제를 지금까지 방치한 것이 너무나도 놀라울 따름! 이번 기회로 이 내용에 대해서 정리해보고, 나중에 또 기회가 되는대로 내용을 더 추가하기로 한다.

     

    쿠키의 기초부터 알고 가자

    우선 쿠키에 대한 기초부터 다시 정리해보고자 한다. 쿠키는 기본적으로 name=value로 이루어진 하나의 값이다. 쿠키를 사용하는 이유는 HTTP가 기본적으로는 stateless 프로토콜이기 때문이다. 클라이언트-서버 요청 간에 상태를 공유하지 않기 때문에, 반드시 정보를 공유해야만 구현할 수 있는 기능(예를 들어 로그인 세션)을 구현하려면 별도의 수단이 필요했던 것이다.

     

    쿠키와 관련해서 각각 서버와 클라이언트에서 설정하는 헤더는 다음과 같다.

     

    Set-Cookie

    Set-Cookie는 서버에서 설정하는 응답 헤더이다. 이 헤더는 쿠키를 저장하고 요청에 포함하는 방식에 대해서 클라이언트(브라우저)에게 내리는 일종의 지시이다. Set-Cookie에서 설정하는 속성은 다음과 같은데, 자세한 내용은 여기를 참고하면 좋을 것 같다.

    • <cookie-name>=<cookie-value>
    • Expires=<date>
    • Max-Age=<number>
    • Domain=<domain-value>
    • Path=<path-value>
    • Secure
    • HttpOnly
    • SameSite=<samesite-value>

     

    Cookie

    Set-Cookie를 통해 쿠키를 전달받은 클라이언트는 쿠키를 저장하고, 정해진 지시에 따라 다음 요청부터 Cookie 헤더에 쿠키를 설정하여 요청한다. Set-Cookie와 달리 따로 설정하는 속성은 없고 name1=value1; name2=value2; ...와 같은 형식으로 쿠키를 요청에 담아 보낸다.

     

    SameSite 설정은 무엇인가?

    위에서 언급한 Set-Cookie 헤더의 속성 중에서 SameSite가 바로 이 글의 주인공이다. SameSite는 브라우저가 cross-origin 요청에 대해서 쿠키를 보낼지 여부를 결정한다. SameSite에는 3가지의 값을 설정할 수 있는데 각 옵션의 의미를 알기 전에, 'same site'가 무엇인지부터 알아보면 좋을 것 같다.

     

    이 글이 origin과 site의 의미에 대한 좋은 길잡이가 되는 것 같은데, 글의 설명을 빌려 정리하자면 다음과 같다.

     

    origin

    origin은 scheme(protocol) + host + port의 조합을 가리킨다. 

    예시 1

    후술하겠지만 same-origin policy라는 정책이 있는데, same-origin이 존재한다는 것은, 같지 않은 origin, 즉 cross-origin이 존재한다는 것을 의미한다. origin은 scheme, host, port가 모두 일치해야 same-origin이다. 하나라도 다르면 cross-origin이다.

     

    site

    site는 TLD(Top-Level Domain)와 그 직전의 domain의 조합을 가리킨다. (아래의 두 예시에서 eTLD+1을 가리킨다)

    예시 2
    예시 3

    TLD는 .com, .org처럼 도메인 주소의 가장 마지막 부분이다. 그런데 예시3처럼 my-project.github.io의 경우 .io만을 TLD로 분류하면, 그 직전의 domain만으로 site를 유일하게 식별할 수 없다. 따라서 eTLD(effective TLD)가 정의되었고, 그 리스트가 관리되고 있다.

     

    origin과 마찬가지로 site에도 same-site와 cross-site가 있다. origin과 달리 site에서는 subdomain과 port가 달라도 '(직전 도메인).(eTLD)'만 같다면 same-site이다. scheme(protocol)이 달라도 원래는 same-site였는데, 이제는 정의를 좀 더 엄격하게 하여 scheme까지 같아야 same-site로 간주된다. 즉 http://www.example.com은 https://www.example.com과 cross-site인 것이다. 

     

    (참고: Sec-Fetch-Site라는 요청 헤더가 있는데, 이 헤더는 요청 origin과 응답 origin의 관계를 알려주는 메타데이터 헤더이다. 그 값으로는 same-origin, same-site, cross-site, none이 있다.)

     

    그렇다면 Set-Cookie의 SameSite 속성의 각 설정값은 어떤 의미일까? 

     

    Strict

    이름에서 알 수 있듯이 가장 엄격한 정책이다. 브라우저가 same-site 요청에 대해서만 쿠키를 보내도록 한다. A 사이트에서 발급한 쿠키 X가 SameSite=Strict일 때, A 사이트에서 발생하는 요청에 대해서만 X를 설정한다. 다른 site에서 발생하는 요청에 대해서는 X가 설정되지 않는다.

     

    Lax

    이름에서 알 수 있듯이 Strict보다 조금 느슨해진 정책이다. 기본적으로 cross-site 요청에 대해서는 브라우저가 쿠키를 보내지 않지만, 외부 site에서 origin site로 이동하면서 발생하는 요청이라면 쿠키를 보낸다. <a>, <form>에서의 get 메소드 정도에 대해서만 허용되는 것이라고 한다(출처). 만약 따로 SameSite 속성을 설정하지 않았다면 SameSite=Lax가 기본값이다.

     

    None

    이름에서 알 수 있듯이 가장 관대한 정책이다. 브라우저가 same-site뿐만 아니라 cross-site 요청에서도 쿠키를 보낸다. 하지만 모든 요청에 대해서 쿠키를 보내는 것은 아니다. SameSite=None을 사용하려면 Secure도 반드시 설정되어야 한다. Secure은 https 요청에 대해서만 쿠키를 설정하는 옵션이다. 즉 None 옵션에서도 최소한의 보안은 보장하고자 하는 것이다.

     

    이전에 SameSite 속성의 디폴트는 Secure이 필요없는 None이었다. same-site의 조건에도 scheme이 들어가지 않았다. 하지만 최근 몇 년 사이에 보안이 강화되어 Lax로 디폴트가 변경되었고, None을 사용한다면 Secure이 강제되었으며, scheme이 다르다면 cross-site인 것으로 판단하게 되었다.

     

    브라우저의 디폴트가 SameSite=Lax로 변경된 이유

    그럼 브라우저가 SameSite=Lax로 방어하고자 하는 것은 무엇일까? 바로 csrf(또는 xsrf, Cross-Site Request Forgery)라는 보안 공격이다. 간략히 설명하자면, csrf는 1) 공격 대상이 되는 사이트에, 2) 공격자가 필요로 하는 권한이 있고 로그인되어 있는 사용자로 하여금, 3) 공격자에게 이득이 되는 행동을 하게 하는 것이다. (자세한 내용은 여기를 참고하면 좋을 것 같다.)

     

    csrf 공격 방식이 쿠키를 이용하는 만큼 SameSite=Lax로 보안 수준을 올리는 것이 도움이 된다. 몇 가지 예외를 제외하고는 cross-site 요청에 대해서는 쿠키가 보내지지 않으므로 공격에 노출될 위험이 줄어드는 것이다. (참고로 프론트엔드 개발을 하다보면 만나게 되는 same-origin policy도 csrf에 대한 방어책이다.)

     

    다시 내 이슈로 돌아와서

    내가 담당하는 기능은 A site에서 B site로 요청을 하는데, 요청되는 기능을 사용하기 위해서는 인증 여부가 반드시 확인이 되어야 하고, 인증에는 쿠키가 사용되는 방식이다. 즉 요청에 반드시 쿠키가 포함되어야 하는 기능이었다. SameSite 값은 기존에 따로 설정한 바가 없었다. 따라서 브라우저의 정책이 SameSite=Lax로 바뀜에 따라 브라우저가 쿠키를 전송하지 않게 되었고, 해당 기능이 제대로 작동하지 않게 된 것이다. 

     

    해결은 위에서 간단하다. SameSite=None; Secure로 바꿔주면 된다. 다만 내가 담당한 기능의 경우 HTTP를 사용하지 않고 있어 Secure을 사용할 수 없었고, 이로 인해 HTTPS로도 전환해야 했다.

     

    사실 이 과정에서 미봉책도 하나 알게 되었다. 보안을 낮추는 행위이니까 원칙적으로는 사용하면 안 될 것 같지만, 파이어폭스의 경우 SameSite=Lax를 디폴트로 하는 기능을 off할 수 있다. 

     

    방법은 다음과 같다.  

    1. 파이어폭스 주소창에 about:config를 입력한다.
    2. 다음과 같은 경고 메시지가 뜬다: "주의해서 사용하세요. 고급 구성 설정을 변경하면 Firefox의 성능 또는 보안에 영향을 줄 수 있습니다." 
    3. 과감하게 "위험을 감수하고 계속 진행" 버튼을 누른다.
    4. 설정 검색창을 통해 network.cookie.sameSite.laxByDefault를 찾는다. true로 되어 있는 값을 false로 바꾼다.
    5. 이제 cross-site 간에 잘 전송되지 않던 쿠키가 잘 전송된다!

    사파리에서도 환경설정 > 개인 정보 보호에서 '크로스 사이트 추적 방지'와 '모든 쿠키 차단'에 체크를 해제하면 같은 효과를 얻을 수 있다. 다만 파이어폭스처럼 정확히 저 디폴트 기능을 on/off하는 방식이 아니라서, 저 두 옵션을 체크 해제했을 때 정확히 어떤 설정 상태인 것인지 잘 모르겠다. 크롬에서도 분명 방법이 있을텐데 못 찾았다.

     

    하지만 이 방식은 보안이 약화되는 것이니까 되도록 안 쓰는 것이 좋겠다. 신뢰하는 사이트에 대해서만 사용하고 바로 디폴트 값으로 복구하는 것이 좋을 것 같다.

    'network, security' 카테고리의 다른 글

    server throttling을 알아보자  (0) 2022.05.01
    OAuth란?  (0) 2021.09.20

    댓글

Designed by Tistory.