이번 시간에는 HTTP cookie에 대해 알아보도록 하겠습니다. cookie는 상당히 중요한 개념이라서 면접 때 물어볼 확률이 높은 편이라고 생각합니다. localStorage와 비교해서 말이죠. 아무튼 cookie는 실무에서도 자주 사용되는 기술이라서 잘 알아두는게 좋습니다. 특히 여러 (보안)옵션이 있는데 이런 부분 미리 살펴보는게 좋겠죠?
HTTP 쿠키(웹 쿠키, 브라우저 쿠키)는 서버가 사용자의 웹 브라우저에 전송하는 작은 데이터 조각입니다. 브라우저는 그 데이터 조각들을 저장해 놓았다가, 동일한 서버에 재 요청 시 저장된 데이터를 함께 전송합니다. 이를 이용하면 사용자의 로그인 상태를 유지할 수 있습니다. 상태가 없는(stateless) HTTP 프로토콜에서 상태 정보를 기억시켜주기 때문입니다.
쿠키는 소프트웨어가 아닙니다. 쿠키는 컴퓨터 내에서 프로그램처럼 실행될 수 없으며 바이러스를 옮길 수도, 악성코드를 설치할 수도 없습니다. 하지만 스파이웨어를 통해 유저의 브라우징 행동을 추적하는데에 사용될 수 있고, 누군가의 쿠키를 훔쳐서 해당 사용자의 웹 계정 접근권한을 획득할 수도 있습니다. (그래서 상당히 중요하고 잘 알아둘 필요가 있습니다)
과거엔 클라이언트 측에 정보를 저장할 때 쿠키를 주로 사용하곤 했습니다. 쿠키를 사용하는 게 데이터를 클라이언트 측에 저장할 수 있는 유일한 방법이었을 때는 이 방법이 타당했지만, 지금은 modern storage APIs를 사용해 정보를 저장하는 걸 권장합니다. 모든 요청마다 쿠키가 함께 전송되기 때문에, (특히 mobile data connections에서) 성능이 떨어지는 원인이 될 수 있습니다. 정보를 클라이언트 측에 저장하려면 Modern APIs의 종류인 웹 스토리지 API (localStorage와 sessionStorage) 와 IndexedDB 를 사용하면 됩니다.
그리고 쿠키는 4,096바이트 크기 제한이 있습니다. 따라서 더 큰 데이터를 저장할 때는 웹 스토리지 API를 사용해야 합니다. 쿠키는 개수 제한도 있습니다. 하나의 도메인 당 20개의 쿠키만을 가질 수 있습니다.
쿠키는 다음의 요소로 구성됩니다. 심플하죠? 키-값 쌍인 형태로 Dictionary 혹은 객체와 유사한 구조입니다.
쿠키는 주로 세 가지 목적을 위해 사용됩니다: - 참고
기업들은 구매 습관에 대한 정보를 수집하기 위해 쿠키를 추적함으로써 사용자들의 웹 습관을 악용한다. 월스트리트저널은 미국의 상위 50개 웹사이트가 평균 64개의 추적 기술을 컴퓨터에 설치해 총 3,180개의 추적 파일을 만들었다고 밝혔다. 그 후 데이터는 수집되어 입찰 법인에 판매될 수 있다. (진짜 대단하다...)
쿠키는 간단한 방식으로 동작합니다. 이해하는데 크게 어렵지 않습니다.
HTTP 요청을 수신할 때, 서버는 응답과 함께 Set-Cookie 응답 헤더를 전송할 수 있습니다. 이 서버 헤더는 클라이언트에게 쿠키를 저장하라고 전달합니다.
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
[page content]
이제, 서버로 전송되는 모든 요청과 함께, 브라우저는 Cookie 헤더를 사용하여 서버로 이전에 저장했던 모든 쿠키들을 회신할 것입니다.
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
여기서 주의할 점은 쿠키가 브라우저 자체에 저장되는 건 아니라는 것입니다. 쿠키가 실제 저장되는 장소는 당연히 내 컴퓨터 안에 어딘가에 존재합니다. 저는 MacOS를 사용하므로 이를 기준으로 크롬 브라우저에 저장된 쿠키를 찾아보도록 하겠습니다.
위치 : /Users/사용자명/Library/Application Support/Google/Chrome/Default
해당 위치에 가서 보면 Cookies 파일이 존재합니다. 근데 그냥 열어보려고 하면 다 깨져서 나옵니다. 찾아보니 형식 자체가 SQLite로 되어있어서 SQLite Viewer로 볼 수 있다고 하네요. 번거롭지만 설치해줬습니다.
그리고 나서 보니까 Finder 내에 숨긴 폴더 때문에 SQLite Viewer에 경로를 찾지 못하더군요. 찾아보니 Command(⌘) + Shift + .
로 숨긴 폴더를 볼 수 있습니다. 드디어 SQLite Viewer로 열어볼 수 있었습니다.
아래는 교보문고에서 저장한 쿠키인걸로 보이네요. (은근 많네...)
근데 특이하게 value가 다 없네요. 실제 크롬 브라우저에서 확인해봤을때는 value가 있는데... 왜 여기서는 확인이 안될까요? (음...🤔) 아. encrypted_value라는게 따로 있네요. 뭔가 암호화가 되어있나봅니다.
쿠키와 다양한 쿠키의 옵션을 다루는 일은 쉽지 않습니다. 하지만 옵션을 설정하는 일은 보안과 관련이 되어있기 때문에 중요합니다. 지금부터 차근차근 알아보도록 하겠습니다.
httpOnly 옵션은 웹서버에서 Set-Cookie 헤더를 이용해 쿠키를 설정할 때 지정할 수 있습니다. 이 옵션은 자바스크립트 같은 클라이언트 측 스크립트가 쿠키를 사용할 수 없게 합니다.
res.cookie("session_id", "12345", {
httpOnly: true,
path: "/",
});
아래와 같이 document.cookie를 통해 쿠키를 볼 수도 없고 조작할 수도 없습니다.
이렇게 설정하는 이유는 해커가 악의적인 자바스크립트 코드를 페이지에 삽입하고 사용자가 그 페이지에 접속하기를 기다리는 방식의 공격(XSS)을 예방하기 위해서입니다. 근본적으로 악의적인 코드가 삽입하지 못하도록 예방해야 하지만, 실수나 버그로 인해 있을 확률이 있기 때문에 해커가 코드를 삽입할 가능성이 있습니다.
이런 상황이 만에 하나 발생하면, 해커가 document.cookie를 볼 수 있고 조작도 할 수 있습니다. 쿠키엔 주로 인증 정보가 있어서 해커가 이 정보를 훔치거나 조작할 수 있게 됩니다. 그렇기 때문에 httpOnly 옵션을 설정하는 것이 보안상 좋습니다.
Domain 그리고 Path 디렉티브는 쿠키의 스코프를 정의합니다.
Domain은 쿠키가 전송되게 될 호스트들을 명시합니다. 만약 명시되지 않는다면, (서브 도메인은 포함되지 않는) 현재 문서 위치의 호스트 일부를 기본값으로 합니다. 도메인이 명시되면, 서브도메인들은 항상 포함됩니다. 만약 Domain=mozilla.org
이 설정되면, 쿠키들은 developer.mozilla.org
와 같은 서브도메인 상에 포함되게 됩니다.
Path는 Cookie 헤더를 전송하기 위하여 요청되는 URL 내에 반드시 존재해야 하는 URL 경로입니다. ("/") 문자는 디렉티브 구분자로 해석되며 서브 디렉토리들과 잘 매치될 것입니다. 만약 Path=/docs
이 설정되면, /docs
, /docs/web/
, /docs/web/HTTP
모두 매치가 됩니다.
아래는 간단하게 path 경로를 '/'가 아닌 '/products'로 설정을 해봤습니다.
res.cookie("session_id", "12345", {
httpOnly: true,
path: "/products",
});
그랬더니 '/'에서는 Set-Cookie 응답 헤더는 넘어오는데 cookie가 안보이더군요. URL를 '/products'로 이동하니까 그제서야 cookie가 보였습니다. 그니까 경로가 매치가 되어야 Cookie 헤더를 전송하는 거네요.
쿠키의 라이프타임은 두가지 방법으로 정의할 수 있습니다:
아래는 세션 쿠키 입니다.
이번에는 Expires를 사용해서 영속적인 쿠키를 만들어보겠습니다. 브라우저는 설정된 유효 일자까지 쿠키를 유지하다가, 해당 일자가 도달하면 쿠키를 자동으로 삭제합니다.
// 지금으로부터 60초 후에 만료되는 쿠키
const date = new Date(Date.now() + 60 * 1000);
// cookie setting
res.cookie("session_id", "12345", {
httpOnly: true,
path: "/",
expires: date,
});
흠. Set-Cookie 응답 헤더를 다시 받으면 업데이트가 되네요. 만약 업데이트가 안되면 해당 유효기간 이후에는 자동으로 삭제가 됩니다. (확인해봄)
Max-Age는 좀 더 사용하기 쉽게 만들어졌습니다. expires 옵션의 대안으로, 현재부터 설정하고자 하는 만료일시까지의 시간을 초로 환산한 값을 설정합니다. 여기는 초 단위네요. 아, 근데 node.js res.cookie에서의 maxAge는 밀리초 단위인거 같네요. (사람 헷갈리게 하고 있어)
// cookie setting
res.cookie("session_id", "12345", {
httpOnly: true,
path: "/",
maxAge: 60 * 1000,
});
참고로, Expires와 Max-Age가 모두 설정되어 있으면 Max-Age가 우선합니다.
유용한 팁으로는 0 또는 음수로 설정하면 쿠키를 즉시 만료(파기)시킬 수 있습니다. 로그아웃 시킬때 유용하게 사용됩니다.
Secure 쿠키는 HTTPS 프로토콜 상에서 암호화된(encrypted) 요청일 경우에만 전송됩니다. 안전하지 않은 HTTP(localhost 제외)와 함께 전송되지 않으므로 중간자 공격자가 쉽게 액세스할 수 없습니다. - 참고. 아... localhost에서 테스트를 해봤더니 암호화된 요청이 아니더라도 전송이 되길래 왜 이러지 싶었는데 localhost는 제외였군요.
참고로, Secure이 쿠키의 중요한 정보에 대한 모든 액세스를 차단한다고 가정하지 마십시오. 예를 들어, 클라이언트의 하드 디스크(또는 HttpOnly가 설정되지 않은 경우 자바스크립트)에 접근할 수 있는 사람은 정보를 읽고 수정할 수 있습니다. 당연하겠죠?
참고 : https://ko.javascript.info/cookie (여기가 볼만 하다)
cross-site 요청과 함께 쿠키를 보내는지 여부를 제어합니다. cross-site request forgery(CSRF) 공격에 대한 보호 기능을 제공합니다. SameSite 쿠키는 여전히 실험 중이며 모든 브라우저에 의해 아직 제공되지 않고 있습니다.
일단, CSRF에 대해 간단히 알아보도록 하겠습니다. (나중에 별도로 자세히 다루겠습니다)
참고 : Your App Is NOT Secure If You Don’t Use CSRF Tokens
아... 이 영상을 보면 CSRF가 어떤식으로 동작하는지 알 수 있을 거 같습니다. 그니까 사용자가 back.com 사이트에 로그인해서 은행 업무를 보고 있었다고 가정해봅시다. 이때 쿠키를 통해 서버가 사용자를 식별해서 요청을 처리하고 있습니다. 사용자는 은행 업무가 끝나고 다른 사이트를 접속하는데 그게 하필 아래와 같은 악의적인 코드가 있다면 어떨까요? back.com 사이트를 통해 저장된 쿠키가 같이 전송되면서 해당 요청이 실행됩니다. 결국에는 사용자가 자신의 의지와 무관하게 공격자가 의도한 행위를 하게 된 것입니다. 이것이 CSRF의 핵심.
<form action="http://back.com/delete-user" method="POST"></form>
<script>
const form = document.querySelector("form");
form.submit();
</script>
생각해보면 CORS 에러가 발생되지 않을까 싶습니다. 왜냐면 Same Origin이 아니니까요... 하지만 form action에 대해 cors 관련해서 찾아보니 form action은 자바스크립트가 포함되지 않아 SOP 정책이 적용되지 않는다고 하네요. (헉!!) - stackoverflow 참고
흠... 왜 이건 SOP 정책이 적용되지 않는지 찾아보니까 이것은 HTML 양식이 CORS 규격보다 이전이기 때문이라고 하네요. CORS가 도입되었을 때 그들은 기존 웹사이트 기능을 잠재적으로 파괴하지 않고서는 이러한 행동을 바꿀 수 없었다.... 보안의 허점인가요?? 🤔 - 참고
어쨌거나 CSRF 공격을 막기 위해 전통적으로 CSRF 토큰이라는 것을 사용했습니다. 하지만 여기서는 쿠키의 SameSite 옵션만으로도 (이론상으로) 크로스 사이트 요청 위조를 막을 수 있다는 것을 보도록 하겠습니다.
samesite=strict
: 사용자가 사이트 외부에서 요청을 보낼 때, 해당 옵션이 있는 쿠키는 절대로 전송되지 않습니다. 다시 말해, 동일한 사이트 요청, 즉 쿠키를 설정한 동일한 사이트에서 발생한 요청에 대해서만 쿠키를 전송함을 의미합니다. 메일에 있는 링크를 따라 접속하거나 악의적인 사이트에서 폼을 전송하는 경우 등과 같이 제3의 도메인에서 요청이 이뤄질 땐 쿠키가 전송되지 않습니다. 이 보호장치는 꽤 믿을 만합니다. 다만 약간의 불편함도 감수해야 합니다. 참고 영상에서 확인 바람. - 참고samesite=lax
: 사용자 경험을 해치지 않으면서 XSRF 공격을 막을 수 있는 느슨한 접근법입니다. 마찬가지로 사이트 외부에서 요청을 보낼 때 브라우저가 쿠키를 보내는 걸 막아줍니다. 하지만 예외사항이 존재합니다. “안전한” HTTP 메서드인 경우(예: GET 방식). 링크를 따라가는 행위는 항상 GET 방식입니다. 두번째는 작업이 최상위 레벨 탐색에서 이루어질 때(브라우저 주소창에서 URL을 변경하는 경우) 입니다. 브라우저를 이용해 자주 하는 작업인 "특정 URL로 이동하기"를 실행하는 경우, samesite=lax 옵션이 설정되어 있으면 쿠키가 서버로 전송됩니다. 하지만 외부 사이트에서 AJAX 요청을 보내거나 폼을 전송하는 등의 복잡한 작업을 시도할 때는 쿠키가 전송되지 않습니다.SameSite 옵션만으로 CSRF 를 막을 수 있어서 간단하지만 아직 실험적인 기능이고 오래된 브라우저(2017년 이전 버전)에선 지원하지 않는 단점이 있습니다. 따라서 samesite 옵션으로만 보안 처리를 하게 되면, 구식 브라우저에서 보안 문제가 발생할 수 있습니다.
참고 : https://youtu.be/LHSSY8QNvew?si=hHa1y9JWdCUWI_m7&t=228
쿠키에는 크게 퍼스트파티 쿠키와 서드 파티 쿠키가 있습니다.
쿠키는 동일한 인터넷 도메인의 서버 또는 서버 설정에만 전송되지만, 웹 페이지는 다른 도메인의 서버에 저장된 이미지 또는 기타 구성 요소를 포함할 수 있습니다. 이러한 구성 요소를 검색하는 동안 설정된 쿠키를 서드 파티 쿠키라고 합니다. 서드 파티 쿠키는 주소 표시줄에 표시된 도메인과 다른 도메인에 속합니다. 이러한 종류의 쿠키는 일반적으로 웹 페이지가 배너 광고와 같은 외부 웹 사이트의 콘텐츠를 특징으로 할 때 나타납니다. 이는 사용자의 브라우징 이력을 추적할 수 있는 가능성을 열어주고, 광고주가 각 사용자에게 관련 광고를 제공하는 데 사용됩니다.
참고 : https://en.wikipedia.org/wiki/Web_banner (웹 배너 광고 끔찍...)
예를 들어, 사용자가 www.example.org
을 방문한다고 가정해봅니다. 이 웹사이트에는 ad.foxytracking.com
로 부터 광고가 들어있습니다. 이 광고를 다운로드 받으면 광고의 도메인에 속하는 쿠키가 설정됩니다. 그런 다음, 사용자는 www.foo.com
를 방문했더니 여기에도 같은 광고의 도메인에 배너가 있습니다. 결국, 이 두 쿠키는 광고를 싣거나 그들의 웹사이트를 방문할 때 광고주에게 보내질 것 입니다.
참고 : https://en.wikipedia.org/wiki/HTTP_cookie#Third-party_cookie
가상의 예에서, 한 광고 회사는 두 개의 웹사이트에 배너를 설치했습니다. 광고 회사는 배너 이미지를 서버에 호스팅하고 서브 파티 쿠키를 사용함으로써 이 두 사이트에 걸친 사용자의 브라우징을 추적할 수 있습니다.
개인 정보를 중요시 하는 현대 사회에서 서드 파티 쿠키는 논란을 일으키기 충분했습니다. 대부분의 최신 웹 브라우저는 서드 파티 쿠키를 차단할 수 있는 개인 정보 보호 설정을 포함하고 있습니다. 2020년 기준으로는 사파리, 파이어폭스에서 이러한 기능을 제공하고 있습니다
구글 크롬에서는 구글은 당초 2020년 초 개인정보보호(Privacy Sandbox) API 테스트 이니셔티브로 불리는 크롬의 서드파티 추적 쿠키 지원을 단계적으로 폐지한다는 계획을 발표했습니다. 본격적인 서드 파티 쿠키 차단은 2024년 하반기가 될 거 같군요. - 참고
서드파티 쿠키를 사용하여 여러 도메인에 걸쳐 추적을 수행할 때 사용자의 프로파일을 구축할 가능성은 프라이버시 위협입니다. 이런 이유로, 몇몇 국가들은 쿠키에 관한 법률을 제정하고 있습니다. 제3자의 쿠키 사용을 소비자에게 공개하지 않는 웹사이트 운영자는 쿠키 사용이 적발될 경우 소비자의 신뢰를 해칠 우려가 있습니다.
쿠키를 법으로 제정한 경우는 EU가 대표적입니다. EU의 쿠키법(Cookie Law, 또는 쿠키지침(Cookie Directive))는 유럽 의회의 Directive 2009/136/EC에 정의되어 있으며 2011년 5월 25일에 발효되었습니다.
이러한 규정에는 다음과 같은 요구사항이 포함됩니다.
어느 순간부터 사이트에서 쿠키 동의에 관한 팝업이 자주 보이기 시작했는데 이것은 EU 쿠키 디렉티브 영향이라고 볼 수 있습니다. 아래 예시는 stackoverflow에서 보여주는 쿠키 사용 동의 여부 팝업입니다.
쿠키 종류별로 커스터마이징 설정이 가능하게도 되어있습니다.
쿠키에 대해서 다룰 주제(내용)은 정말 많은 거 같습니다. 그만큼 쿠키는 중요한 녀석인 것이죠. 보안상에도 중요하고, 개인정보 측면에서도 중요합니다.