🍪

[요약 정리] A JavaScript developer’s guide to browser cookies

태그
요약프론트엔드Web
최종 편집
Dec 30, 2022 2:32 AM
발행일
November 5, 2021

쿠키란 무엇인가?

  • 쿠키는 세션 관리, 개인화, 유저 행동 추적 등에 쓰인다.
  • 이전에는 쿠키가 모든 클라이언트 정보 저장을 담당하기도 했지만, 쿠키는 해당 도메인의 서버에 보내는 모든 요청에 함께 가기 때문에 성능 이슈가 있으며, 도메인별로 갯수 및 용량 제한이 있다(대개 20개, 4kb).
  • 요즘은 보통 Web Storage APIs를 사용한다. 이들은 5MB까지도 가능하다.

쿠키 접근하고 할당하기

  • 클라이언트(브라우저)에서는 document.cookie 로 접근하고 할당할 수 있다.
    • document.cookie = "hello=world; domain=example.com; Secure";
    • 위와 같이 한다고 쿠키 값이 완전히 덮어써지는 건 아니다. 같은 키의 값이 있으면 업데이트해줄 뿐.
    • 헷갈리므로 js-cookie 같은 패키지를 쓰는 게 좋다. TypeScript 지원도 된다.
  • 서버(Node.js)에서는 HTTP 요청의 request header로 접근하고, response header로 수정할 수 있다.
    • 브라우저가 보낸 쿠키는 요청 헤더에 cookie 라는 key로, 값은 key=value; 형태로 들어있다.
    • http.createServer(function (request, response) {
          var cookies = request.headers.cookie;
          // "cookie1=value1; cookie2=value2"
          ...
      }).listen(8124);
    • 쿠키를 브라우저에 원하는 값으로 할당할 때는 response 헤더에 Set-Cookie 헤더를 쓰면 된다.
    • response.writeHead(200, {
          'Set-Cookie': 'mycookie=test; domain=example.com; Secure'
      });
    • 클라이언트의 경우와 마찬가지로, cookie-parser 같은 미들웨어를 쓰면 더 편하다. TypeScript 지원도 된다.

쿠키의 속성들

  • Domain: 브라우저에게 어떤 호스트가 쿠키 값을 읽을 수 있는지 지정해준다. 없으면 쿠키를 Set한, 동일 호스트만 읽을 수 있으며, 같은 도메인을 가진 서버 API를 호출할 때만 쿠키가 함께 움직인다.
    • 물론 이게 있다고 아무 도메인에서나 쿠키 값을 읽을 수 있는 건 아니다. 단지 서브도메인에게 쿠키 값을 허용할 것인지 정도의 의미가 있다.
    • 예를 들어 내 현재 도메인이 abc.xyz.com 인데 Domain 속성을 지정하지 않았다면 정확히 이 도메인에서만 쿠키를 읽을 수 있지만, Domain=xyz.com 을 설정해두면 다른 def.xyz.com 도 읽을 수 있다.
    • 아무 값이나 여기에 넣을 수 있는 것은 아니다. .com 같은 TLD, .co.uk 같은 유사 TLD로 세팅된 Domain= 은 대부분의 브라우저에서 무시된다.
    • 과거에는 이 목록이 각 브라우저 벤더에서 내부적으로 관리해서 inconsistency가 있었으나, 요즘은 Mozilar에서 Public Suffix List 프로젝트를 발족해서 이걸 전체 벤더가 공유한다. 이 목록에는 github.iovercel.app 같은 서비스도 들어있다.
  • Path: Domain과 유사하게, 여기 명시된 하위 경로에서만 쿠키 접근이 가능하게 한다. 예를 들어 Path=/store 가 정의된 쿠키는 /store, /store/cart 등에서만 접근 가능하다.
  • Expires: 쿠키가 언제 이후로 폭파될지 정의한다. 일정 주기로 광고를 보여주는 등의 유즈케이스에서 유용하다. 과거 시각으로 설정함으로써 쿠키를 삭제할 때도 쓸 수 있다(쿠키를 삭제하는 방법으로 스펙에 명시되어 있다).
  • Secure: localhost를 제외하고, HTTPS를 통해야만 서버에 전송되게 한다. 항상 설정하는 게 좋다.
  • HTTPOnly: 서버에서만 쿠키 접근과 설정이 가능하게 한다. 인증 토큰처럼 민감 정보를 담을 때 유용하다.
    • XSS 공격이 완전히 막히는 건 아니다. 악의적인 써드파티 스크립트가 서버로 API 요청을 보낼 때에도 인증 토큰이 함께 담겨서 가기 때문.
  • SameSite
    • 써드파티 사이트에서도 이론상 내 서버에 API 요청을 할 수 있고, 이 때 내 도메인과 일치하는 쿠키는 함께 전송된다. 이런 쿠키를 써드파티 쿠키라고 부른다. 이는 유튜브 embed 따위에서는 유용하나, 그 외 거의 모든 경우에서는 안전하지 않다. 악의적인 사이트에서 내 서버로 API 호출할 때에도 내 정보가 함께 담길 것이고, 이런 걸 CSRF 공격이라고 부른다.
    • SameSite는 이를 막기 위해 2016년에 제안된 속성이다. SameSite를 이용해 퍼스트 파티, 즉 도메인이 일치하는 URL에서 요청할 때만 쿠키를 붙여주게 할 수 있다.
      • 참고로 a.github.iob.github.io 는 SameSite가 아니고, my-site.comsub.my-site.com 은 SameSite다.
    • SameSite=Strict: 퍼스트 파티에게만 간다.
    • SameSite=Lax: Strict보다 살짝 완화된 기준이다. 써드파티에서 퍼스트 파티로 유저 클릭이나 폼 전송 따위에 의한 top-level 네비게이션을 했을 때 전송된다.
      • 2020년 초부터 시작해서 대부분의 메이저 브라우저에서 SameSite의 기본값을 Lax 로 지정하기 시작했다.
    • SameSite=None: 과거와 같음.
    • (MDN 문서, web.dev 아티클, SameSite=Lax를 다룬 블로그 글도 참고.)

프라이버시와 써드파티 쿠키

  • 여러 웹사이트에 걸쳐 추적되어 개인화된 광고를 띄우는 기작은 대략 이러하다.
    • 어떤 웹사이트에 써드파티 서비스를 위한 스크립트나 iframe embed가 추가된다.
    • 그 서비스에게 API 요청을 하면, 본인의 도메인에 대한 쿠키를 HTTP 응답으로 지정할 수 있다.
    • 이 쿠키는, 그 써드파티를 embed하는 다른 웹사이트에서 동일하게 접근할 수 있다.
    • 이 쿠키를 이용해 당신을 여러 웹사이트에 걸쳐 인지하고, 개인화 광고를 뿌린다.
  • 이를 막기 위해 Firefox를 비롯한 많은 브라우저들이 ETP(Enhanced tracking protection)라는 기능을 통해 유명한 써드파티 추적 쿠키를 금지하기 시작했다.
  • 그러나 추적 쿠키의 목록이 불완전하므로, 브라우저에서는 아예 써드파티 쿠키를 제거하려는 계획을 세우고 있다.
  • 문제는 이렇게 하면 추적 외에 실제로 유용하게 쓰일 수 있는 써드파티 쿠키까지 막힌다는 건데, 그래서 새로운 API인 Storage Access라는 것도 제시되었다. 사용자의 명시적 허용을 통해서만 써드파티 쿠키를 허용하는 것.