쿠키 파헤치기

쿠키와 웹 스토리지(로컬 스토리지, 세션 스토리지)의 차이점, 쿠키의 옵션, 쿠키와 세션의 차이점, 세션 기반 인증 방식과 토큰 기반 인증 방식의 차이점을 알아보겠습니다.

이 시리즈는 총 4개의 글에 나눠 작성합니다.

쿠키는 매우 중요한 만큼 3개의 글에 걸쳐 등장합니다.
Keyplus를 개발하면서도 JWT를 저장하는 데에 쿠키를 사용하였으나 제대로 알고 사용하지 않았습니다. 이번 기회에 제대로 알아보려 합니다.

  1. 로컬 스토리지, 세션 스토리지 그리고 쿠키 + IndexedDB
  2. 쿠키 파헤치기 👈🏻
  3. 쿠키와 세션 + 캐시
  4. 세션 기반 인증과 토큰 기반 인증

쿠키 (그리고 로컬 스토리지와 세션 스토리지)

1편에서 못다 한 로컬 스토리지와 세션 스토리지와 연관된 쿠키에 대한 설명을 이어 해보겠습니다.

로컬 스토리지와 세션 스토리지가 나오기 이전에도 브라우저에 저장소 역할을 하는 것이 있었습니다. 바로 쿠키입니다. 쿠키는 만료 기한이 있는 키-값 저장소입니다.

쿠키는 브라우저에 저장되는 작은 크기의 문자열로, HTTP 프로토콜의 일부입니다.

쿠키는 주로 웹 서버에 의해 만들어집니다. 서버가 HTTP 응답 헤더의 Set-Cookie에 내용을 넣어 전달하면 브라우저는 이 내용을 자체적으로 브라우저에 저장합니다. 이게 바로 쿠키입니다. 브라우저는 사용자가 쿠키를 생성하도록 한 서버(사이트)에 접속할 때마다 쿠키의 내용을 Cookie 요청 헤더에 넣어서 함께 전달합니다.

HTTP 요청은 상태를 가지고 있지 않습니다. 브라우저에서 서버로 나에 대한 정보를 가져오라는 GET /me라는 요청을 보낼 때, 서버는 요청 자체만으로는 그 요청이 누구에게서 오는지 알 수 없습니다. 이때 쿠키에 나에 대한 정보를 담아서 서버로 보내면 서버는 쿠키를 읽어서 내가 누군지 파악합니다. 이처럼 쿠키는 서버와 클라이언트 간의 지속적인 데이터 교환을 위해 만들어졌기 때문에 한 번 서버에 의해 쿠키가 심긴 후에는 요청마다 자동으로 서버에 계속 전송됩니다.

쿠키는 4kb 용량 제한이 있습니다. 만약 4kb 용량 제한을 거의 다 채운 쿠키가 있다면, 요청을 할 때마다 기본 4kb의 데이터를 사용합니다. 4kb 중에는 서버에 필요하지 않은 데이터들도 있을 것이고, 그렇다면 데이터 낭비가 발생하게 됩니다. 바로 그런 데이터들을 로컬 스토리지와 세션 스토리지에 저장할 수 있습니다. 이 두 저장소의 데이터는 서버로 자동 전송되지 않기 때문입니다.

쿠키란?

쿠키는 주로 웹 서버에 의해 만들어집니다. 서버가 HTTP 응답 헤더의 Set-Cookie에 내용을 넣어 전달하면, 브라우저는 이 내용을 자체적으로 브라우저에 저장합니다. 이게 바로 쿠키입니다. 브라우저는 사용자가 쿠키를 생성하도록 한 동일 서버(사이트)에 접속할 때마다 쿠키의 내용을 Cookie 요청 헤더에 넣어서 함께 전달합니다.

쿠키는 주로 사용자 인증, 커스터마이징 옵션 등의 저장에 사용됩니다.

브라우저를 끄면 사라지는 휘발성 쿠키를 의미합니다.

브라우저의 메모리에 저장되어 브라우저가 열려 있는 동안만 유효한 쿠키입니다.

쿠키에 만료일이 포함되어 있지 않은 경우 세션 쿠키로 저장됩니다.

실무에서는 이것을 세션이라 부릅니다.

브라우저를 종료해도 사라지지 않는 쿠키를 의미합니다.

하드디스크에 파일로 저장되어 브라우저를 종료해도 유효하고, 만료 시기가 되면 삭제됩니다.

쿠키에 만료일이 포함되어 있는 경우 영구 쿠키로 저장됩니다.

실무에서는 이것을 쿠키라 부릅니다.


사용자가 현재 접속한 사이트와 도메인이 일치하는 쿠키를 의미합니다.

사용자가 현재 접속한 사이트와 다른 도메인의 쿠키를 의미합니다. 즉, 방문 사이트가 아닌 제3자가 심어놓은 쿠키를 의미합니다. 주로 광고 서버에서 발급돼 이용 정보를 수집하고, 개인맞춤형 광고를 제공하기 위해 활용돼 왔습니다.


First-party Cookie와 Third-party Cookie는 아래에서 SameSite 옵션 설명 시 더 자세하게 다룹니다.


쿠키의 옵션

Path

Path=/mypath

이 옵션에 설정한 경로 혹은 하위 경로에 있는 페이지만 쿠키에 접근(쿠키를 전송)할 수 있습니다. 미 지정 시 기본값은 현재 경로입니다.

ex) path=/admin 옵션을 사용하여 설정한 쿠키는 /admin과 /admin/something에서는 볼 수 있지만, /home이나 /adminpage에서는 볼 수 없습니다.

특별한 경우가 아니라면, path 옵션을 path=/과 같이 루트로 설정해서 웹 사이트의 모든 페이지에서 쿠키에 접근할 수 있도록 하는 것이 좋습니다.

Domain ⭐️⭐️

Domain=site.com

쿠키에 접근 가능한(쿠키를 전송할) 도메인을 지정합니다. 다만, 몇 가지 제약이 있어 아무 도메인이나 지정할 수는 없습니다.

domain 옵션을 설정하지 않으면 기본값으로 쿠키를 보낸 서버의 도메인이 설정되고, 쿠키를 설정한 도메인에서만 쿠키에 접근할 수 있습니다. 즉, 서브도메인에서 접근 불가능합니다. 반면, 도메인을 명시하면 서브도메인에서 접근 가능합니다. 즉, 도메인을 명시하는 것이 명시하지 않는 것보다 덜 제한적입니다.


site.com에서 설정한 쿠키는 other.com에서 얻을 수 없습니다.
즉, 다른 도메인(쿠키를 설정한 도메인과 아예 다른 도메인)이 심은 쿠키에 접근할 수 있는 방법은 없습니다. (다른 도메인을 가진 서버로 전송되지 않습니다.) - CORS와 다른 얘기입니다.

또한 서브 도메인인 forum.site.com에서도 쿠키 정보를 얻을 수 없습니다.
그러나 서브 도메인으로의 쿠키 전송은 쿠키 옵션으로 해결할 수 있습니다. site.com에서 쿠키를 설정할 때 domain 옵션에 루트 도메인인 Domain=site.com을 명시적으로 설정해주면 서브 도메인에서도 쿠키에 접근할 수 있습니다.

Domain 옵션은 자기 자신 혹은 자기 자신의 상위 도메인으로만 설정 가능하며, 그렇지 않으면 브라우저가 해당 쿠키를 무시합니다. - Share cookie between subdomain and domain 참고

하위 호환성 유지를 위해 (site.com 앞에 점을 붙인) domain=.site.com도 domain=site.com과 동일하게 작동합니다. 오래된 표기법이긴 하지만 구식 브라우저를 지원하려면 이 표기법을 사용하는 것이 좋습니다.

이렇게 domain 옵션값을 적절히 사용하면 서브 도메인에서도 쿠키에 접근할 수 있습니다.


Keyplus를 예시로 들어 보겠습니다.

Keyplus의 서버api.keyplus.kr입니다. Keyplus의 클라이언트keyplus.kr입니다.

  1. 서버에서 쿠키 설정 시 domian을 명시하지 않았다면 자동적으로 api.keyplus.kr이 설정되고, api.keyplus.kr이 아닌 다른 도메인을 가진 서버(서브 도메인 포함(ex. a.api.keyplus.kr))을 보낼 때 그 쿠키가 자동으로 함께 보내지지 않습니다. 즉, 해당 쿠키를 설정한 서버에게 요청을 보낼 때만 그 쿠키가 보내집니다. 이를 host only cookie라고 합니다.
  2. 서버에서 쿠키 설정 시 domain을 keyplus.kr이라고 명시했다면 api.keyplus.kr(Keyplus 서버)는 물론, keyplus.kr 또는 a.keyplus.kr 도메인을 가진 서버에게 요청을 보낼 때 그 쿠키가 자동으로 함께 보내집니다.
  3. 서버에서 쿠키 설정 시 domain을 example.com이라고 명시했다면 쿠키가 브라우저에 저장되지 않습니다. 위에서 설명했듯이 도메인 옵션은 자신 혹은 자기 자신의 상위 도메인으로만 설정 가능하며, 그렇지 않으면 브라우저가 해당 쿠키를 무시하기 때문입니다.
  4. 서버에서 쿠키 설정 시 domain을 a.api.keyplus.kr이라고 명시했다면 쿠키가 브라우저에 저장되지 않습니다. 위에서 설명했듯이 도메인 옵션은 자신 혹은 자기 자신의 상위 도메인으로만 설정 가능하며, 그렇지 않으면 브라우저가 해당 쿠키를 무시하기 때문입니다.

즉, 쿠키의 domain 옵션은 클라이언트에 이미 심긴 쿠키가 어떤 도메인을 가진 서버로 보내질 것이냐에 관한 문제입니다. 클라이언트에 쿠키가 심길 것이냐 말 것이냐는 CORS와 관련된 문제입니다.
(물론 위에서 설명했듯이 도메인 옵션에 자신 혹은 상위 도메인으로 입력하지 않는다면 브라우저가 해당 쿠키를 무시하므로 심기지 않는다는 측면에서는 관련이 있긴 합니다.)

Keyplus의 경우, 서버인 api.keyplus.kr이 쿠키를 심고, 또 그 서버로 쿠키를 보내려 하므로 domain에 관한 설정은 따로 안 해주어도 됩니다.

Expires & Max-Age ⭐️

max-age나 expires 옵션이 지정되어 있지 않으면 브라우저가 닫힐 때 쿠키도 함께 삭제됩니다. 이런 쿠키를 세션 쿠키라고 부릅니다.

max-age나 expires 옵션이 지정되어 있다면 브라우저가 닫혀도 만료 시간이 되기 전까지 쿠키는 삭제되지 않습니다. 이런 쿠키를 영구/지속 쿠키라 부릅니다.

Expires=Tue, 19 Jan 2038 03:14:07 GMT

브라우저는 expires에 적힌 일자가 도달하면 쿠키를 자동으로 삭제합니다. 쿠키의 유효 일자는 반드시 GMT 포맷으로 설정해야 합니다. new Date(0) 혹은 과거로 지정하면 쿠키는 바로 삭제됩니다.

Max-Age=3600

max-age는 expires 옵션의 대안으로, 쿠키 만료 기간을 설정할 수 있게 해줍니다. 현재부터 설정하고자 하는 만료일시까지의 시간을 초로 환산한 값을 설정합니다. 0 혹은 음수값 설정 시 쿠키는 바로 삭제됩니다.

Secure ⭐️

Secure

이 옵션을 설정하면 HTTPS로 통신하는 경우에만 쿠키가 전송됩니다.

secure 옵션이 없으면 기본 설정이 적용되어 http://site.com에서 설정(생성)한 쿠키를 https://site.com에서 읽을 수 있고, https://site.com에서 설정(생성)한 쿠키도 http://site.com에서 읽을 수 있습니다. 쿠키는 기본적으로 domain만 확인하고 프로토콜을 따지지는 않기 때문입니다. (웹 스토리지는 origin을 기준으로 판단하는 것과 다릅니다.)

하지만 secure 옵션이 설정된 경우, https://site.com에서 설정한 쿠키는 http://site.com에서 접근할 수 없습니다. 쿠키에 민감한 내용이 저장되어 있어 암호화되지 않은 HTTP 연결을 통해 전달되는 걸 원치 않는다면 이 옵션을 사용하면 됩니다.

접속한 사용자를 식별하기 위해 사용하는 session__id 등의 쿠키는 Secure 옵션을 설정하는 것이 좋습니다. session__id만 탈취한다면 다른 사람이 나인 것처럼 흉내 내는 것이 가능하기 때문입니다.

SameSite ⭐️⭐️

다른 도메인에서의 쿠키 전송에 관한 보안을 설정하는 옵션입니다.

일단 이 SameSite라는 것이 무엇인지부터 알아볼 필요가 있습니다.

여기서 말하는 동일한 사이트란 Public Suffix와 그 이전의 도메인 부분이 같은 사이트를 의미합니다. 즉, a.example.comb.example.com은 동일한 사이트입니다. 여기에서 .com이 public suffix이고, example.com이 사이트입니다.

.com만 본다면 Public Suffix를 Top-level domain(TLD)라고 생각할 수 있지만 그렇지 않습니다.

Public Suffix List를 확인해보면 알 수 있듯이 .github.io는 그 자체로도 public suffix에 등록되어 있습니다. 그렇기에 .io가 기준이 아니라 .github.io가 기준이 됩니다. 즉, a.github.iob.github.io는 cross-site입니다. 그리고, a.a.github.io, b.a.github.io는 same-site입니다.
.com같은 Top-level domain과 .github.io처럼 Top-level domain은 아니지만 public suffix로 인정되어 site를 규정하는 것을 합쳐서 effective TLD(eLD)라고 부릅니다.

eTLD(1) eTLD(2)

Understanding “same-site” and “same-origin”에서 자세하게 설명해주고 있습니다. domain과 origin의 개념을 안다면 쉽게 이해할 수 있습니다.




CORS가 이름과 달리(?) 크로스 오리진 간 요청을 허용해주는 정책이었던 것처럼 SameSite 옵션도 이름과 달리 크로스 사이트 간 쿠키 전송을 막는 옵션입니다.

사실 이 옵션과 상관 없이 SameSite 간 쿠키는 언제나 전송됩니다. 즉, 현재 접속한 사이트와 요청을 보내고자 하는 사이트의 도메인이 같다면(1차, 2차 도메인) 항상 전송됩니다.

이를 이해하기 위해서는 First-party Cookie와 Third-party Cookie에 대한 이해가 먼저 있어야 합니다.

<html>
  <head>
    <title>site.com</title>
  </head>
  <body>
    <img src="https://other.com/image.png" />
  </body>
</html>

사용자가 site.com에 접속해 있을 때, 위처럼 other.com이 제공하는 이미지를 사용하고 있다면 사용자는 site.com에 접속해 있지만 브라우저에서는 other.com/image.png로 요청을 보낼 것입니다.

이때 other.com에 대한 쿠키를 가지고 있다면, 해당 쿠키가 other.com을 운영하는 서버로 같이 전송됩니다.

이때 전송되는 쿠키를 바로 서드파티 쿠키라고 합니다. Referer 헤더와 쿠키에 설정된 도메인이 다른 쿠키라고도 말할 수 있습니다.

<html>
  <head>
    <title>site.com</title>
  </head>
  <body>
    <!-- 아래 링크를 클릭한 경우에 전송되는 쿠키들은 서드파티 쿠키로 취급됩니다. -->
    <a href="https://other.com/">링크</a>
  </body>
</html>

마찬가지로 site.com에 있는 other.com 링크를 클릭한 경우에 전송되는 쿠키도 서드파티 쿠키입니다.

즉, 같은 쿠키라도 사용자가 접속한 페이지에 따라 퍼스트 파티 쿠키로도, 서드파티 쿠키로도 부를 수 있습니다.

앞의 예제에서 other.com에 설정된 쿠키는 사용자가 site.com에 접속해 있을 때는 서드파티 쿠키였지만, other.com에 접속해 있을 때는 퍼스트 파티 쿠키입니다.


혼동하면 안 되는 것이 있습니다!

이는 위에서 Domain 옵션을 설명할 때 말한 쿠키를 얻을 수 있다 없다랑 다른 얘기입니다.

퍼스트 파티 쿠키와 서드파티 쿠키는 쿠키를 설정한 도메인과 얻을 수 있는 도메인에 관한 얘기가 아니라 현재 사용자가 접속한 사이트와 요청하려는(쿠키를 전송하려는) 서버에 관한 얘기입니다.


SameSite 옵션은 CSRF 공격을 막기 위해 만들어진 옵션입니다. (서드파티 쿠키의 보안적 문제를 해결하기 위해 만들어진 옵션)

이 옵션엔 strict, lax, none 3가지 값을 설정할 수 있습니다.

SameSite=Strict

가장 보수적인 정책으로, Strict로 설정된 쿠키는 크로스 사이트 요청에는 항상 전송되지 않습니다. 즉, 서드파티 쿠키는 전송되지 않고, 퍼스트 파티 쿠키만 전송됩니다.

SameSite=Lax

Strict에 비해 상대적으로 느슨한 정책으로, 대체로 서드파티 쿠키는 전송되지 않지만, 몇 가지 예외적인 요청에는 전송됩니다. (안전한 HTTP 메서드(e.g. GET 등)인 경우 또는 작업이 최상위 레벨 탐색에서 이루어지는 경우(브라우저 주소창에서 URL을 변경하는 경우(e.g. a 태그의 href, link 태그의 href 등))

SameSite 설정이 없다면 default 설정인 Lax가 적용됩니다.

SameSite=None

SameSite가 탄생하기 전 쿠키가 동작하던 방식과 동일하게 동작합니다. 크로스 사이트 요청의 경우에도 항상 전송됩니다. 즉, 서드파티 쿠키도 전송됩니다.

SameSite=None 설정 시 Secure 옵션 설정은 필수입니다.

Keyplus의 경우, 클라이언트는 keyplus.kr, 서버는 api.keyplus.kr이므로 SameSite입니다. 그리고 사용자는 keyplus.kr에 접속하여 api.keyplus.kr로 요청을 보내므로 쿠키는 항상 전송됩니다. SameSite 옵션을 strict로 설정해주어도 됩니다.


크롬은 2023년 말 서드파티 쿠키 지원을 중단한다고 합니다. 즉, 모든 쿠키가 SameSite=Strict로 설정된 것처럼 동작하게 될 것이라는 의미입니다.

당초 예정했던 2022년 초에서 2년의 시간을 늦춘 것인데, 사람들은 반대가 많았기에 내린 결정이 아닌가 추측하고 있습니다. 특히 디지털 광고 생태계를 뒤흔들 것이라 예상된다고 합니다.

HttpOnly ⭐️

HttpOnly

이 옵션은 브라우저에서 JavaScript로 접근할 수 없도록 하는 옵션입니다. document.cookie를 통해 쿠키를 볼 수도 없고, 조작할 수도 없습니다.

즉, 이 옵션이 설정된 쿠키는 서버에 전송되는 것만 가능합니다.

이를 통해 XSS 공격으로 쿠키가 탈취되는 것을 예방할 수 있습니다.

해커가 악의적인 자바스크립트 코드를 페이지에 삽입하고 사용자가 그 페이지에 접속하기를 기다리는 방식의 공격을 예방할 때 이 옵션을 사용합니다.

Secure 옵션과 마찬가지로, session__id 등의 중요한 쿠키에는 이 옵션을 설정해주는 것이 좋습니다.

참고


Written by정선아
🌱 공부한 것을 기록하여 성장하기 위한 블로그입니다.

GitHubGmail