27일 8월 2020

쿠키와 document.cookie

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

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

쿠키는 클라이언트 식별과 같은 인증에 가장 많이 쓰입니다.

  1. 사용자가 로그인하면 서버는 HTTP 응답 헤더의 Set-Cookie에 담긴 “세션 식별자(session identifier)” 정보를 사용해 쿠키를 설정합니다.
  2. 사용자가 동일 도메인에 접속하려고 하면 브라우저는 HTTP Cookie 헤더에 인증 정보가 담긴 고윳값(세션 식별자)을 함께 실어 서버에 요청을 보냅니다.
  3. 서버는 브라우저가 보낸 요청 헤더의 세션 식별자를 읽어 사용자를 식별합니다.

document.cookie 프로퍼티를 이용하면 브라우저에서도 쿠키에 접근할 수 있습니다.

쿠키와 다양한 쿠키의 옵션을 다루는 일은 쉽지 않습니다. 이 챕터에선 이에 대해 자세히 알아보도록 하겠습니다.

쿠키 읽기

지금 보고 있는 이 사이트와 관련된 쿠키가 브라우저에 저장되어있는지 알아봅시다.

// javascript.info에선 Google Analytics(GA)를 사용해 데이터를 수집하고 있습니다.
// 이와 관련된 쿠키를 확인해 봅시다.
alert( document.cookie ); // cookie1=value1; cookie2=value2;...

document.cookiename=value 쌍으로 구성되어있고, 각 쌍은 ;로 구분합니다. 이때, 쌍 하나는 하나의 독립된 쿠키를 나타냅니다.

;을 기준으로 document.cookie의 값을 분리하면 원하는 쿠키를 찾을 수 있습니다. 정규 표현식이나 배열 관련 함수를 함께 사용해서 말이죠.

이에 관한 연습문제를 아래에서 풀어보길 권유합니다. 쿠키 조작에 쓰이는 몇 가지 헬퍼(도우미) 함수를 챕터 끝에 명시해 놓았으니, 이를 참고하셔도 좋습니다.

쿠키 쓰기

document.cookie에 직접 값을 쓸 수 있습니다. 이때 cookie는 데이터 프로퍼티가 아닌 접근자(accessor) 프로퍼티입니다. 앞서 프로퍼티 getter와 setter에서 학습한 바와 같이, 접근자 프로퍼티에 값을 할당하는 것은 데이터 프로퍼티에 값을 할당하는 것과는 조금 다르게 처리됩니다.

document.cookie에 값을 할당하면, 브라우저는 이 값을 받아 해당 쿠키를 갱신합니다. 이때, 다른 쿠키의 값은 변경되지 않습니다.

아래와 같이 코드를 작성하면 이름이 user인 쿠키를 찾아 그 값을 John으로 갱신합니다.

document.cookie = "user=John"; // 이름이 'user'인 쿠키의 값만 갱신함
alert(document.cookie); // 모든 쿠키 보여주기

코드를 실행하면 여러 개의 쿠키가 출력되는 것을 확인할 수 있습니다. 이를 통해 document.cookie= 연산은 모든 쿠키를 덮어쓰지 않고, 명시된 쿠키인 user의 값만 갱신한 것을 알 수 있습니다.

쿠키의 이름과 값엔 특별한 제약이 없기 때문에 모든 글자가 허용됩니다. 하지만 형식의 유효성을 일관성 있게 유지하기 위해 반드시 내장 함수 encodeURIComponent를 사용하여 이름과 값을 이스케이프 처리해 줘야 합니다.

// 특수 값(공백)은 인코딩 처리해 줘야 합니다.
let name = "my name";
let value = "John Smith"

// 인코딩 처리를 해, 쿠키를 my%20name=John%20Smith 로 변경하였습니다.
document.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value);

alert(document.cookie); // ...; my%20name=John%20Smith
쿠키의 한계

쿠키엔 몇 가지 제약 사항이 있습니다.

  • encodeURIComponent로 인코딩한 이후의 name=value 쌍은 4KB를 넘을 수 없습니다. 이 용량을 넘는 정보는 쿠키에 저장할 수 없습니다.
  • 도메인 하나당 저장할 수 있는 쿠키의 개수는 20여 개 정도로 한정되어 있습니다. 개수는 브라우저에 따라 조금씩 다릅니다.

쿠키엔 몇 가지 옵션이 있습니다. 몇몇 옵션은 아주 중요하기 때문에 꼭 지정해 줘야 합니다.

옵션은 key=value 뒤에 나열하고 ;로 구분합니다. 아래와 같이 말이죠.

document.cookie = "user=John; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT"

path

  • path=/mypath

URL path(경로)의 접두사로, 이 경로나 이 경로의 하위 경로에 있는 페이지만 쿠키에 접근할 수 있습니다. 절대 경로이어야 하고, (미 지정시) 기본값은 현재 경로입니다.

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

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

domain

  • domain=site.com

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

domain 옵션에 아무 값도 넣지 않았다면, 쿠키를 설정한 도메인에서만 쿠키에 접근할 수 있습니다. site.com에서 설정한 쿠키는 other.com에서 얻을 수 없죠.

이 외에 까다로운 제약사항이 하나 더 있습니다. 서브 도메인(subdomain)인 forum.site.com에서도 쿠키 정보를 얻을 수 없다는 점입니다.

// site.com에서 쿠키를 설정함
document.cookie = "user=John"

// site.com의 서브도메인인 forum.site.com에서 user 쿠키에 접근하려 함
alert(document.cookie); // 찾을 수 없음

서브 도메인이나 다른 도메인에서 쿠키에 접속할 방법은 없습니다. site.com에서 생성한 쿠키를 other.com에선 절대 전송받을 수 없습니다.

이런 제약사항은 안정성을 높이기 위해 만들어졌습니다. 민감한 데이터가 저장된 쿠키는 관련 페이지에서만 볼 수 있도록 하기 위해서 말이죠.

그런데 정말 forum.site.com과 같은 서브 도메인에서 site.com에서 생성한 쿠키 정보를 얻을 방법이 없는 걸까요? 방법이 있습니다. site.com에서 쿠키를 설정할 때 domain 옵션에 루트 도메인인 domain=site.com을 명시적으로 설정해 주면 되죠.

// site.com에서
// 서브 도메인(*.site.com) 어디서든 쿠키에 접속하게 설정할 수 있습니다.
document.cookie = "user=John; domain=site.com"

// 이렇게 설정하면

// forum.site.com와 같은 서브도메인에서도 쿠키 정보를 얻을 수 있습니다.
alert(document.cookie); // user=John 쿠키를 확인할 수 있습니다.

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

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

expires와 max-age

expires(유효 일자)나 max-age(만료 기간) 옵션이 지정되어있지 않으면, 브라우저가 닫힐 때 쿠키도 함께 삭제됩니다. 이런 쿠키를 "세션 쿠키(session cookie)"라고 부릅니다.

expiresmax-age 옵션을 설정하면 브라우저를 닫아도 쿠키가 삭제되지 않습니다.

  • expires=Tue, 19 Jan 2038 03:14:07 GMT

브라우저는 설정된 유효 일자까지 쿠키를 유지하다가, 해당 일자가 도달하면 쿠키를 자동으로 삭제합니다.

쿠키의 유효 일자는 반드시 GMT(Greenwich Mean Time) 포맷으로 설정해야 합니다. date.toUTCString을 사용하면 해당 포맷으로 쉽게 변경할 수 있습니다. 아래는 유효 기간이 하루인 쿠키를 만드는 예시입니다.

// 지금으로부터 하루 후
let date = new Date(Date.now() + 86400e3);
date = date.toUTCString();
document.cookie = "user=John; expires=" + date;

expires 옵션값을 과거로 지정하면 쿠키는 삭제됩니다.

  • max-age=3600

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

0이나 음수값을 설정하면 쿠키는 바로 삭제됩니다.

// 1시간 뒤에 쿠키가 삭제됩니다.
document.cookie = "user=John; max-age=3600";

// 만료 기간을 0으로 지정하여 쿠키를 바로 삭제함
document.cookie = "user=John; max-age=0";

secure

  • secure

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

secure 옵션이 없으면 기본 설정이 적용되어 http://site.com에서 설정(생성)한 쿠키를 https://site.com에서 읽을 수 있고, https://site.com에서 설정(생성)한 쿠키도 http://site.com에서 읽을 수 있습니다.

쿠키는 기본적으로 도메인만 확인하지 프로토콜을 따지진 않기 때문입니다.

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

// (https:// 로 통신하고 있다고 가정 중)
// 설정한 쿠키는 HTTPS 통신시에만 접근할 수 있음
document.cookie = "user=John; secure";

samesite

또 다른 보안 속성인 samesite 옵션은 크로스 사이트 요청 위조(cross-site request forgery, XSRF) 공격을 막기 위해 만들어진 옵션입니다.

아래 XSRF 공격 시나리오를 통해 이 속성의 동작 방식과 언제 이 속성을 유용하게 사용할 수 있는지 알아보도록 합시다.

XSRF 공격

현재 bank.com에 로그인되어있다고 가정해 봅시다. 해당 사이트에서 사용되는 인증 쿠키가 브라우저에 저장되고, 브라우저는 bank.com에 요청을 보낼 때마다 인증 쿠키를 함께 전송할 것입니다. 서버는 전송받은 쿠키를 이용해 사용자를 식별하고, 보안이 필요한 재정 거래를 처리합니다.

이제 (로그아웃하지 않고) 다른 창을 띄워서 웹 서핑을 하던 도중에 뜻하지 않게 evil.com에 접속했다 가정해 봅시다. 이 사이트엔 해커에게 송금을 요청하는 폼(form) <form action="https://bank.com/pay">이 있고, 이 폼은 자동으로 제출되도록 설정되어 있습니다.

폼이 evil.com에서 은행 사이트로 바로 전송될 때 인증 쿠키도 함께 전송됩니다. bank.com에 요청을 보낼 때마다 bank.com에서 설정한 쿠키가 전송되기 때문입니다. 은행은 전송받은 쿠키를 읽어 (해커가 아닌) 계정 주인이 접속한 것이라 생각하고 해커에게 돈을 송금합니다.

이런 공격을 크로스 사이트 요청 위조라고 부릅니다.

실제 은행은 당연히 이 공격을 막을 수 있도록 시스템을 설계합니다. bank.com에서 사용하는 모든 폼에 "XSRF 보호 토큰(protection token)"이라는 특수 필드를 넣어서 말이죠. 이 토큰은 악의적인 페이지에서 만들 수 없고, 원격 페이지에서도 훔쳐 올 수 없도록 구현되어 있습니다. 따라서 악의적인 페이지에서 폼을 전송하더라도 보호 토큰이 없거나 서버에 저장된 값과 일치하지 않기 때문에 요청이 무용지물이 됩니다.

하지만 이런 절차는 구현에 시간이 걸린다는 단점을 수반합니다. 모든 폼에 보호 토큰을 세팅해줘야 하죠. 또한 요청 전부를 검수해야 합니다.

samesite 옵션

쿠키의 samesite 옵션을 이용하면 “XSRF 보호 토큰” 없이도 (이론상으로) 크로스 사이트 요청 위조를 막을 수 있습니다.

이 옵션엔 두 가지 값을 설정할 수 있습니다.

  • samesite=strict(값을 설정하지 않고 그냥 samesite 옵션만 써줘도 동일하게 동작함)

사용자가 사이트 외부에서 요청을 보낼 때, samesite=strict 옵션이 있는 쿠키는 절대로 전송되지 않습니다.

메일에 있는 링크를 따라 접속하거나 evil.com과 같은 사이트에서 폼을 전송하는 경우 등과 같이 제3의 도메인에서 요청이 이뤄질 땐 쿠키가 전송되지 않죠.

인증 쿠키에 samesite 옵션이 있는 경우, XSRF 공격은 절대로 성공하지 못합니다. evil.com에서 전송하는 요청엔 쿠키가 없을 것이고, bank.com은 미인식 사용자에게 지급을 허용하지 않을 것이기 때문입니다.

이 보호장치는 꽤 믿을 만합니다. bank.com에서 수행하는 모든 작업은 samesite 쿠키를 함께 전송하기 때문이죠.

하지만 약간의 불편함도 감수해야 합니다.

만약 사용자가 메모장 등에 bank.com에 요청을 보낼 수 있는 링크를 기록해 놓았다가 이 링크를 클릭해 접속하면 bank.com이 사용자를 인식하지 못하는 상황이 발생하기 때문입니다. 실제로 이런 경우 samesite=strict 옵션이 설정된 쿠키는 전송되지 않습니다.

이런 문제는 쿠키 두 개를 함께 사용해 해결할 수 있습니다. "Hello, John"과 같은 환영 메시지를 출력해주는 "일반 인증(general recognition)"용 쿠키, 데이터 교환 시 사용하는 samesite=strict 옵션이 있는 쿠키를 따로 둬서 말이죠. 이렇게 하면 외부 사이트를 통해 접근한 사용자도 정상적으로 환영 메시지를 볼 수 있습니다. 지급은 무조건 은행의 사이트를 통해서만 수행되도록 만들면 됩니다.

  • samesite=lax

samesite=lax는 사용자 경험을 해치지 않으면서 XSRF 공격을 막을 수 있는 느슨한 접근법입니다.

strict와 마찬가지로 lax도 사이트 외부에서 요청을 보낼 때 브라우저가 쿠키를 보내는 걸 막아줍니다. 하지만 예외사항이 존재합니다.

아래 두 조건을 동시에 만족할 때는 samesite=lax 옵션을 설정한 쿠키가 전송됩니다.

  1. “안전한” HTTP 메서드인 경우(예: GET 방식. POST 방식은 해당하지 않음).

    안전한 HTTP 메서드 목록은 RFC7231 명세에서 확인할 수 있습니다. 안전한 메서드는 읽기 작업만 수행하고 쓰기나 데이터 교환 작업은 수행하지 않습니다. 참고로, 링크를 따라가는 행위는 항상 GET 방식이기 때문에 안전한 메서드만 쓰입니다.

  2. 작업이 최상위 레벨 탐색에서 이루어질 때(브라우저 주소창에서 URL을 변경하는 경우).

    대다수의 작업은 이 조건을 충족합니다. 하지만 <iframe>안에서 탐색이 일어나는 경우는 최상위 레벨 탐색이 아니기 때문에 이 조건을 충족하지 못합니다. AJAX 요청 또한 탐색 행위가 아니므로 이 조건을 충족하지 못합니다.

브라우저를 이용해 자주 하는 작업인 "특정 URL로 이동하기"를 실행하는 경우, samesite=lax 옵션이 설정되어 있으면 쿠키가 서버로 전송됩니다. 노트에 저장된 링크를 여는 것도 특정 URL로 이동하는 행위이므로 위 조건들을 충족합니다.

하지만 외부 사이트에서 AJAX 요청을 보내거나 폼을 전송하는 등의 복잡한 작업을 시도할 때는 쿠키가 전송되지 않습니다.

이런 제약사항이 있어도 괜찮다면, samesite=lax 옵션은 사용자 경험을 해치지 않으면서 보안을 강화해주는 방법으로 활용할 수 있을 것입니다.

samesite는 좋은 옵션이긴 하지만, 한가지 문제점이 있습니다.

  • 오래된 브라우저(2017년 이전 버전)에선 samesite 옵션을 지원하지 않습니다.

따라서 samesite 옵션으로만 보안 처리를 하게 되면, 구식 브라우저에서 보안 문제가 발생할 수 있습니다.

구식 브라우저에 대응하지 못한다는 문제가 있긴 하지만, samesite 옵션을 XSRF 토큰 같은 다른 보안 기법과 함께 사용하면 보안을 강화할 수 있습니다. 구식 브라우저를 더는 사용하지 않는 때가 오면 XSRF 토큰 역시 필요하지 않겠죠.

httpOnly

이 옵션은 자바스크립트와 전혀 관계가 없지만, 튜토리얼의 완성도를 높이기 위해 잠시 언급하고 넘어가도록 하겠습니다.

httpOnly 옵션은 웹서버에서 Set-Cookie 헤더를 이용해 쿠키를 설정할 때 지정할 수 있습니다.

이 옵션은 자바스크립트 같은 클라이언트 측 스크립트가 쿠키를 사용할 수 없게 합니다. document.cookie를 통해 쿠키를 볼 수도 없고 조작할 수도 없습니다.

해커가 악의적인 자바스크립트 코드를 페이지에 삽입하고 사용자가 그 페이지에 접속하기를 기다리는 방식의 공격을 예방할 때 이 옵션을 사용합니다. 우리가 만든 사이트에 해커가 악의적인 코드를 삽입하지 못하도록 예방해야 하지만, 버그가 있을 확률은 언제나 있기 때문에 해커가 코드를 삽입할 가능성이 있을 수 있습니다.

이런 상황이 만에 하나 발생하면, 사용자가 웹 페이지에 방문할 때 document.cookie를 볼 수 있고 조작도 할 수 있는 해커의 코드도 함께 실행됩니다. 물론 쿠키엔 인증 정보가 있어서 해커가 이 정보를 훔치거나 조작할 수 있게 됩니다. 좋지 않은 상황이 발생하죠.

하지만 httpOnly 옵션이 설정된 쿠키는 document.cookie로 쿠키 정보를 읽을 수 없기 때문에 쿠키를 보호할 수 있습니다.

부록: 쿠키 함수

쿠키를 다룰 때 유용하게 사용할 수 있는 몇 가지 함수를 소개합니다. document.cookie를 수동으로 조작하지 않고 이 함수들을 활용하면 좀 더 편리하게 쿠키를 다룰 수 있습니다.

유사한 기능을 하는 다양한 쿠키 라이브러리가 존재하므로, 이 코드는 데모 목적으로 봐주시면 될 것 같습니다. 데모이지만 실제 환경에서도 동작하는 코드입니다.

getCookie(name)

쿠키에 접근하는 가장 짧은 방법은 정규 표현식(regular expression) 을 사용하는 것입니다.

아래 getCookie(name) 함수는 주어진 name의 쿠키를 반환합니다.

// 주어진 이름의 쿠키를 반환하는데,
// 조건에 맞는 쿠키가 없다면 undefined를 반환합니다.
function getCookie(name) {
  let matches = document.cookie.match(new RegExp(
    "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
  ));
  return matches ? decodeURIComponent(matches[1]) : undefined;
}

위 코드에서 new RegExp; name=<value> 패턴을 찾기 위해 동적으로 생성됩니다.

주의할 점은 쿠키값은 인코딩되어있는 상태이기 때문에 getCookie는 내장 함수인 decodeURIComponent를 이용해 쿠키값을 디코딩한다는 점입니다.

setCookie(name, value, options)

현재 경로(path=/)를 기본으로, 주어진 namevalue를 가진 쿠키를 설정합니다(다른 기본값을 추가할 수 있습니다).

function setCookie(name, value, options = {}) {

  options = {
    path: '/',
    // 필요한 경우, 옵션 기본값을 설정할 수도 있습니다.
    ...options
  };

  if (options.expires instanceof Date) {
    options.expires = options.expires.toUTCString();
  }

  let updatedCookie = encodeURIComponent(name) + "=" + encodeURIComponent(value);

  for (let optionKey in options) {
    updatedCookie += "; " + optionKey;
    let optionValue = options[optionKey];
    if (optionValue !== true) {
      updatedCookie += "=" + optionValue;
    }
  }

  document.cookie = updatedCookie;
}

// Example of use:
setCookie('user', 'John', {secure: true, 'max-age': 3600});

deleteCookie(name)

만료 기간을 음수로 설정하면 쿠키를 삭제할 수 있습니다.

function deleteCookie(name) {
  setCookie(name, "", {
    'max-age': -1
  })
}
갱신이나 삭제는 동일한 도메인과 경로에서만 해야 합니다.

주의: 쿠키를 갱신하거나 삭제할 때는, 쿠키를 설정할 때 지정했던 도메인이나 경로를 사용해야 합니다.

함께 보면 좋은 자료: cookie.js

부록: 서드 파티 쿠키

사용자가 방문 중인 도메인이 아닌 다른 도메인에서 설정한 쿠키를 "서드 파티 쿠키(third-party cookie)"라고 부릅니다.

예시:

  1. site.com의 특정 페이지에서 이미지 배너(banner)를 불러옵니다. 배너는 다른 도메인 <img src="https://ads.com/banner.png"> 에서 가져옵니다.

  2. ads.com에 있는 원격 서버는 배너와 함께 Set-Cookie 헤더를 전송해 브라우저가 id=1234와 같은 쿠키를 설정하도록 합니다. 이 쿠키는 ads.com 도메인에서 설정한 것이기 때문에 ads.com에서만 볼 수 있습니다.

  3. 사용자가 ads.com에 다시 접속하면, 원격 서버는 요청과 함께 전송받은 쿠키의 id를 이용해 해당 유저를 인식합니다.

  4. 사용자가 site.com을 떠나 other.com에 접속하고 이 사이트에도 배너가 있으면 ads.com은 또 쿠키를 전송받습니다. 이 쿠키는 ads.com에서 설정한 것이기 때문이죠. 이를 이용해 ads.com은 사용자를 인식하고, 이 사용자가 어떤 사이트로 이동했는지를 추적합니다.

광고회사는 사용자의 이용 행태를 추적하고, 광고를 제공하기 위해 오래전부터 서드 파티 쿠키를 사용하고 있습니다. 서드파티 쿠키는 쿠키를 설정한 도메인에 종속되기 때문에 ads.com은 사용자가 어떤 사이트를 방문했는지 추적할 수 있습니다.

그런데 사람들은 누군가 자신을 감시하는 걸 좋아하진 않습니다. 브라우저엔 이런 쿠키를 비활성화 할 수 있는 기능이 있는데, 이 기능을 사용하면 추적을 막을 수 있습니다.

여기에 더하여 몇몇 모던 브라우저는 서드 파티 쿠키를 위한 특별한 정책을 도입하여 광고회사의 추적을 막을 수 있게 합니다.

  • Safari는 서드파티 쿠키를 전면적으로 허용하지 않습니다.
  • Firefox는 서드 파티 도메인 "블랙 리스트(black list)"를 만들어 리스트에 오른 도메인의 서드 파티 쿠키를 차단합니다.
주의:

<script src="https://google-analytics.com/analytics.js">같은 태그로 서드 파티 도메인에서 스크립트를 읽어오고, 이 스크립트 안에 document.cookie로 쿠키를 설정하는 코드가 있다면, 이때 만들어진 쿠키는 서드파티 쿠키가 아닙니다.

스크립트에서 쿠키를 설정한 경우에 만들어지는 쿠키는 현재 페이지의 도메인에 속하게 됩니다. 스크립트의 유래와 상관없이 말이죠.

부록: GDPR

이 주제는 자바스크립트와 전혀 관계가 없지만, 쿠키를 설정할 때 명심해야 할 사항입니다.

EU(유럽연합)에는 사용자 개인 정보 보호를 강제하는 법령인 GDPR이 있습니다. 쿠키를 추적하는 경우 사용자로부터 명시적인 허가를 얻어야 한다는 것이 이 법령의 중요 요건 중 하나입니다.

이 요건은 쿠키를 이용한 사용자 추적, 식별에 관한 내용을 담고 있습니다.

따라서 쿠키를 설정하고, 이 쿠키를 정보 저장의 용도로만 사용한다면 이 법령이 강제하는 사항을 지킬 필요가 없습니다. 사용자를 추적하거나 식별하지 않는다면 말이죠.

하지만, 인증 세션과 함께 쿠키를 설정하거나 id를 추적한다면 사용자의 동의를 반드시 얻어야 합니다.

웹 사이트는 다음과 같은 방법으로 GDPR에 대응할 수 있습니다. 이 방법이 적용된 사이트를 접속해 보신 경험이 있으리라 생각합니다.

  1. 인증된 사용자에 대해서만 추적 쿠키를 설정하려는 경우

    가입 양식에 “개인 정보 취급 방침 동의” 같은 확인란을 만들고, 사용자가 이에 동의할 경우에만 추적 쿠키를 설정합니다.

  2. 모든 사용자를 대상으로 추적 쿠키를 설정하려는 경우

    최초 방문자에게 쿠키설정에 대한 동의를 요구하는 "작은 창"을 보여주고, 사용자가 이에 동의한 경우에만 콘텐츠를 표시하고, 추적 쿠키를 설정합니다. 새로운 방문자는 이런 절차가 번거롭다고 생각할 수 있습니다. 콘텐츠를 가리면서 "무조건 클릭해야 하는 창"을 그 누구도 달가워하지 않죠. 하지만 GDPR을 준수하려면 이 창이 반드시 있어야 합니다.

GDPR은 쿠키에 대해서만 다루진 않고, 전반적인 보안 이슈에 관한 내용을 다룹니다. 자세한 사항은 이 튜토리얼의 범위를 벗어나기 때문에 GDPR에 대한 이야기는 여기서 마치도록 하겠습니다.

요약

document.cookie는 쿠키에 접근할 수 있도록 해줍니다.

  • 쓰기는 해당 쿠키의 값만 갱신합니다.
  • 쿠키 이름과 값은 꼭 인코딩해야 합니다.
  • 쿠키 하나가 차지하는 용량은 최대 4KB까지이고, 사이트 하나당 약 20여 개를 허용합니다(브라우저에 따라 다름).

쿠키 옵션:

  • path=/의 기본값은 현재 경로이고, 설정한 경로나 그 하위 경로에서만 쿠키 정보를 볼 수 있습니다.
  • domain=site.com 옵션에 아무런 값을 입력하지 않았다면 쿠키를 설정한 도메인에서만 쿠키 정보를 얻을 수 있습니다. 명시적으로 도메인 주소를 설정한 경우엔, 해당 도메인의 서브 도메인에서도 쿠키 정보를 얻을 수 있습니다.
  • expires/max-age 는 쿠키의 만료 시간을 정해줍니다. 이 옵션이 없으면 브라우저가 닫힐 때 쿠키도 같이 삭제됩니다.
  • secure 는 HTTPS 연결에서만 쿠키를 사용할 수 있게 합니다.
  • samesite 는 요청이 외부 사이트에서 일어날 때, 브라우저가 쿠키를 보내지 못하도록 막아줍니다. XSRF 공격을 막는 데 유용합니다.

추가 사항:

  • 브라우저에 따라 서드 파티 쿠키를 허용하지 않을 수 있습니다. Safari는 기본적으로 서드 파티 쿠키를 금지합니다.
  • 사용자가 EU 국가 거주자인 경우 GDPR을 준수해야 합니다. 따라서, 사용자 추적 시 반드시 동의를 얻어야 합니다.
튜토리얼 지도

댓글

댓글을 달기 전에 마우스를 올렸을 때 나타나는 글을 먼저 읽어주세요.
  • 추가 코멘트, 질문 및 답변을 자유롭게 남겨주세요. 개선해야 할 것이 있다면 댓글 대신 이슈를 만들어주세요.
  • 잘 이해되지 않는 부분은 구체적으로 언급해주세요.
  • 댓글에 한 줄짜리 코드를 삽입하고 싶다면 <code> 태그를, 여러 줄로 구성된 코드를 삽입하고 싶다면 <pre> 태그를 이용하세요. 10줄 이상의 코드는 plnkr, JSBin, codepen 등의 샌드박스를 사용하세요.