development

자바스크립트 브라우저 쿠키 가이드

여름싼타 2022. 9. 19. 19:18
반응형

좋든 싫든 쿠키는 현대 웹 개발에 필수적인 부분이다. 그러나 때로는 웹사이트를 취약하게 만들지 말아야 하거나 속성이 결여된 장소에서 사용되기도 한다. 이 글은 브라우저 쿠키의 작동 방법, 클라이언트와 서버 모두에서 브라우저 쿠키에 접속하여 조작하는 방법 및 브라우저 속성을 사용하여 브라우저에서 쿠키의 가시성을 제어하고 쿠키를 보다 안전하게 만드는 방법에 대한 설명이다. 그리고 써드파티의 쿠키와 관련된 privacy 문제에 대해서도 간략히 다루겠다.

 

쿠키와 쿠키 구조

브라우저 쿠키는 클라이언트 쪽 자바스크립트 또는 HTTP 요청 중에 서버에 의해 생성되는 브라우저에 저장된 작은 데이터이다. 브라우저는 해당 쿠키를 나중에 같은 서버로 보내거나 사용자가 페이지를 다시 방문했을 때 웹 페이지의 클라이언트 측 자바스크립트를 통해 쿠키에 접근할 수 있다. 쿠키는 보통 세션 관리, 개인정보(또는 설정) 및 웹사이트 전체의 사용자 동작을 추적하는 데 사용된다.

 

쿠키가 클라이언트 측의 모든 스토리지에 사용되던 시기가 있었다. 하지만 이 접근 방식에는 문제가 있었다. 도메인의 모든 쿠키는 해당 도메인의 서버로 모든 요구와 함께 전송되기 때문에 특히 저대역폭의 모바일 데이터 연결에서는 성능에 큰 영향을 미칠 수 있다. 같은 이유로 브라우저는 보통 쿠키의 크기와 특정 도메인에 허용되는 쿠키의 수에 제한을 설정한다. (통상 20개 도메인마다 4kb)

최신 Web에는 클라이언트측 스토리지용 WebStorage API(localStorage 및 sessionStorage)이 생겼다. 이를 사용해서 브라우저는 클라이언트 쪽 데이터를 키와 값의 쌍 형태로 저장할 수 있다. 따라서 클라이언트 쪽에서만 데이터를 보유하고 싶다면 이들 API를 사용하는 것이 쿠키보다 훨씬 직관적이고 사용하기 편하며 더 많은 데이터(통상 최대 5MB)를 저장할 수 있는 효과적인 방법이다.

 

쿠키 설정 및 액세스

쿠키는 서버와 클라이언트 모두를 통해 설정 및 접근이 가능하다. 또한 쿠키에는 다양한 속성이 있으며 쿠키에 접속하여 변경할 수 있는 장소와 방법을 결정한다. 이에 대해서는 다음 섹션에서 자세히 설명하겠습니다. 먼저 클라이언트와 서버상의 쿠키에 접속해서 조작하는 방법에 대해 알아보자.

 

클라이언트 (브라우저)

웹사이트에 접속할 때마다 다운로드가 되고 브라우저에서 실행되는 자바스크립트는 일반적으로 클라이언트 쪽의 자바스크립트라고 부른다. Document 속성 cookie를 통해 쿠키에 접근할 수 있다. 즉, document.cookie를 사용하여 현재 위치에서 접근 가능한 모든 쿠키를 읽을 수 있다. key:value 형식의 쿠키 목록을 포함하는 문자열이 표시된다.

 

 

마찬가지로 쿠키를 설정하려면 document.cookie 값을 설정해야 한다. 쿠키의 설정은 key:value 형식이다.

 

 

위 코드는 기존 쿠키를 덮어쓰는 것이 아니다. 동일한 이름의 쿠키가 이미 존재한다면 새 쿠키를 만들거나 기존 쿠키 값을 갱신하기만 하면 된다.
하지만 이것은 깔끔한 코드가 아니다. 클라이언트 쿠키를 처리하기 위해서 래퍼나 js-cookie와 같은 라이브러리를 사용하는 것이 좋다.

 

 

쿠키 상의 CRUD 조작에 대해 깨끗한 API를 제공할 뿐만 아니라 Type Script도 지원하고 있어 attribute의 철자를 피할 수 있다.

 

서버 (Node.js)

서버는 각각 HTTP 요구 요구 헤더와 응답 헤더를 사용하여 쿠키로 액세스 및 변경할 수 있습니다. 브라우저는 HTTP 요청을 서버로 전송할 때마다 관련된 모든 쿠키를 cookie 헤더와 함께 해당 사이트로 전달한다. 사용하고 있는 웹 애플리케이션의 request header를 체크하면 request header가 세미콜론으로 구분된 문자열로 서버로 전송되어 있는 쿠키를 찾을 수 있다.

 

 

다음은 요청 헤더에서 서버 상의 이러한 쿠키를 읽을 수 있다. 예를 들어 서버에서 Node.js를 사용하고 있다면 다음의 스니펫과 같은 request에서 그것들을 읽을 수 있다. 세미콜론으로 구분된 key, value 쌍은 이전에 본 것과 같다.

 

 

마찬가지로 쿠키를 설정하려면 응답 헤더에 Set-Cookie 헤더를 추가한다. 쿠키는 key=value 형식이고, 속성은 세미콜론(존재하는 경우)으로 구분한다. 이것이 Node.js에서 할 수 있는 방법이다.

 


일반적인 Node.js를 사용하지 않을 가능성이 있다. 대신 Express와 같은 웹 프레임워크에서 사용하게 된다. Express를 사용하면 쿠키에 대한 접근과 변경이 훨씬 쉬워진다. 가져오려면 cookie-parser와 같은 미들웨어를 추가하면 req.cookies가 있는 자바스크립트 오브젝트 형식으로 모든 쿠키를 가져올 수 있다. Cookie의 설정에는, Express에 부속의 임베디드 res.cookie() 함수를 사용할 수도 있다.

 

 

모든 코드들은 타입스크립트에서 지원되기 때문에 서버에서도 오탈자가 발생할 가능성은 없다.


쿠키 속성 (attribute)

쿠키 설정 방법과 접근 방법은 쿠키의 속성을 설명하겠다. 이름과 값 쿠키 외에 보안면, 라이프타임, 브라우저에서 접근하는 방법 등 다양한 측면을 제어하는 속성도 있다.

 

도메인

MDN에 따르면 도메인 애트리뷰트는 브라우저에 어떤 호스트가 cookie에 접근할 수 있는지를 알린다. 지정하지 않는 경우, 디폴트로 쿠키를 설정한 것과 같은 호스트가 된다. 따라서 클라이언트 쪽 자바스크립트를 사용하여 쿠키에 접속할 경우 URL 바에 있는 것과 동일한 도메인을 가진 쿠키만이 접근할 수 있다. 마찬가지로 HTTP 요청 도메인과 동일한 도메인을 공유하는 쿠키만이 request 헤더와 함께 서버로 전송된다.

 

이 속성이 있다고 해서 어떤 도메인에 대해서도 쿠키를 설정할 수 있는 것은 아니다. 왜냐하면 그것은 분명히 큰 보안 위험이 되기 때문이다. (evil.com 공격자가 당신의 사이트 awesome.com를 위해 쿠키를 만들고 있다고 상상해봐라.)

 

따라서 이 속성이 존재하는 유일한 이유는 도메인의 제한을 완화하고 서브도메인으로 쿠키에 접근할 수 있도록 하기 위함이다. 예를 들어 현재 도메인이 abc.xyz.com이고 도메인 속성을 지정하지 않을 경우 쿠키를 설정하면 기본적으로 abc.xyz.com이 되고 cookie는 해당 도메인에만 제한된다. 단, 같은 쿠키를 다른 서브도메인에서도 사용할 수 있도록 하려면 Domain=xyz.com를 설정하여 def.xyz.com이나 프라이머리 도메인 xyz.com 등의 다른 서브도메인에서도 사용할 수 있습니다.

 

 

또한 이는 쿠키의 도메인 값을 설정할 수 있음을 의미하는 것이 아니다. com과 같은 TLD나 .co.uk와 같은 의사 TLD는 보안 보호된 브라우저에 의해 무시된다. 당초 브라우저 벤더는 이러한 퍼블릭 도메인 목록을 내부적으로 유지하고 있었기 때문에 브라우저 간에 일관성 없는 동작이 발생하는 것은 불가피했다.

이에 대처하기 위해 Mozilla Foundation은 이 모든 퍼블릭 도메인을 기록하고 벤더 간에 공유하는 퍼블릭 서픽스 리스트라고 불리는 프로젝트를 시작했다. 이 목록에는 github.io이나 vercel.app과 같은 서비스도 포함되어 있다. 이러한 도메인에 쿠키를 설정하는 것을 제한하고 abc.vercel.app과 def를 만들 수 있다. vercel.app은 자체 쿠키세트를 가진 개별 사이트로 카운트된다.

 

Path

이 속성은 쿠키에 접속하기 위해 필요한 요구 URL 경로를 지정한다. 쿠키를 도메인으로 제한하는 것 외에 경로를 통해 제한할 수도 있다. 경로 속성이 Path=/store인 쿠키는 경로/store 및 그 서브패스/store/cart,/store/store/store 등으로만 접근할 수 있다.

 

Expires (만료)

이 속성을 사용하면 쿠키가 파기될 때까지의 유효기간을 설정할 수 있다. 이는 사용자가 인터스티셜 광고를 표시하고 있는지 확인하기 위해 쿠키를 사용하는 경우 유용하다. 또한 1개월 후에 다시 광고를 표시할 수 있도록 쿠키를 1개월 후에 만료로 설정할 수 있다. 또한 과거 Expires 날짜를 설정하여 쿠키를 삭제할 경우에도 사용된다.

 

Secure

Secure 속성을 가지는 쿠키는, 시큐어 HTTPS 프로토콜을 통해서만 서버에 송신되고 HTTP 프로토콜을 통해서 송신되지는 않는다. (로컬 호스트를 제외) 이는 보안 보호되지 않은 연결을 통해 cookie에 접근할 수 없도록 함으로써 ManintheMiddle 공격을 방지하는 데 도움이 된다. 보안 보호되지 않은 HTTP 연결을 통해 웹 사이트에 서비스를 제공하는 경우를 제외하고 모든 쿠키에서 이 속성을 사용해야 한다.

 

HTTPOnly

이 속성은 아마 이름이 나타내듯이 서버를 경유해서만 쿠키에 접속할 수 있다. 따라서 응답 헤더를 경유해서 설정할 수 있는 것은 서버뿐이고 브라우저는 후속 모든 요구 헤더와 함께 그것들을 서버로 전송하고 클라이언트 쪽 자바스크립트를 경유해서 접근할 수 없다.

 

 

이를 통해 클라이언트 측 스크립트로 쿠키(Cookie)를 읽을 수 없기 때문에 인증 토큰 등의 기밀 정보를 포함한 쿠키(Cookie)를 XSS 공격으로부터 보호할 수 있다. 단, XSS 공격으로부터의 완전한 보안을 보증하는 것은 아니다. 이는 공격자가 당신의 웹사이트에서 서드파티 스크립트를 실행할 수 있는 경우 쿠키에 접근하지 못할 수 있지만 대신 관련된 API 요구를 당신의 서버에 직접 실행할 수 있고 브라우저는 당신의 안전한 HTTP Only 쿠키를 요구 헤더와 함께 간단히 첨부하기 때문이다. 해커가 당신의 웹사이트에 악의적인 스크립트를 주입한 페이지에 당신의 사용자 중 한 명이 접속한다고 상상해 봐라. 그들은 해당 스크립트를 사용하여 임의의 API를 실행할 수 있으며, 어느새 사용자를 대신하여 행동할 수 있다.

 

중요한 것은 HTTP 쿠키만이 XSS 공격을 낭비한다고 해도 해커가 당신의 웹사이트에서 스크립트를 실행할 수 있다면 대처해야 할 훨씬 큰 문제가 있기 때문에 완전히 옳지 않다는 것이다. XSS 공격을 막는 방법은 있지만 이 글에서는 다루지 않는다.

 

SameSite

이 글의 첫머리에서 특정 도메인의 쿠키가 지원하는 도메인의 모든 요구와 함께 서버로 전송되는 모습을 확인하였다. 즉, 사용자가 서드파티 사이트에 접속하여 해당 사이트가 도메인 상의 API에 대해 요구를 할 경우, 그 요구에 따라 도메인의 모든 쿠키가 서버로 전송된다. 이것은 당신의 유스케이스에 따라서는 좋은 일이기도 하고 나쁜 일 일수도 있다.

YouTube 임베디드 같은 경우에 편리하다. 따라서 예를 들어 브라우저에서 유튜브에 로그인하고 있는 사용자가 유튜브 채우기를 포함한 서드파티 웹사이트를 방문했을 경우 채우기 버튼을 클릭하여 해당 웹사이트를 벗어날 필요 없이 라이브러리에 추가할 수 있다. 이는 브라우저가 사용자의 인증 상태를 확인하기 위해 youtube.com의 관련 쿠키를 서버로 전송하기 위해 작동한다. 이런 유형의 쿠키는 서드파티 쿠키라고도 불린다.

 

기본적으로 그 이외의 경우는 당신은 그것을 의도하지 않았던 것이다. 사용자가 악의적인 웹사이트에 접속해 해당 웹사이트가 서버로 요구를 전송한 경우, 서버가 요구를 제대로 검증하지 않은 경우 공격자는 사용자 모르게 사용자를 대신해 액션을 실행할 수 있다고 상상해봐라. 이것은 기본적으로 CSRF 공격이라고 불린다.

 

이러한 공격을 막기 위해 2016년에 IETF는 SameSite라는 새로운 속성을 쿠키에 제안했다. 이 속성은 쿠키(Cookie)를 퍼스트 파티의 컨텍스트에만 제한할 수 있다는 점, 즉 URL 바의 도메인이 쿠키(Cookie)의 도메인과 일치할 경우에만 쿠키(Cookie)를 요구에 부가할 수 있음으로써 위의 문제에 대처하는 데 도움이 된다.

 

 

SameSite 속성에는 다음 3종류의 값을 설정할 수 있다.

- strict : strict로 설정하면 쿠키는 퍼스트 파티 컨텍스트에서만 전송된다.
- lax : 이 값은 정상급 내비게이션과 함께 쿠키를 전송할 수 있기 때문에 Strict보다 약간 제한되지 않는다. 즉, 사용자가 구글 검색 결과에서 웹사이트를 클릭하거나 단축 URL을 통해 리다이렉트 된 경우처럼 페이지 요청과 함께 쿠키가 서버로 전송된다.
- none : 이름 그대로 이 속성을 사용하면 앞서 언급한 YouTube 채우기 같은 경우에 사이트 사용자에 관계없이 모든 요청과 관련된 쿠키를 전송함으로써 서드파티 쿠키를 만들 수 있다.

 

프라이버시 및 서드파티제 쿠키

지난 섹션에서 서드 파티의 쿠키에 대해 간략히 설명했다. 즉, 현재 사용하고 있는 사이트 이외의 사이트에 의해 설정된 쿠키는 서드파티 쿠키이다.

 

또한 서드파티 쿠키가 웹사이트 전체에서 당신을 추적하고 개인화된 광고를 표시하는 것으로 얼마나 악명 높은지에 대해서도 들어본 적이 있을지도 모른다. 쿠키의 규칙을 알았으니 그들이 어떻게 그것을 할지 짐작할 수 있을 것이다.

 

기본적으로 웹 사이트가 스크립트를 사용하거나 서드파티 서비스용 iframe을 경유하여 매립을 추가하는 경우 서드파티 서비스는 HTTP 응답 헤더를 사용하여 해당 서비스의 도메인에 cookie를 설정할 수 있다. 또한 이러한 쿠키를 사용하여 동일한 서드파티 서비스의 임베디드를 사용하고 있는 웹사이트 전체를 추적할 수도 있다. 마지막으로, 이러한 서드파티 서비스를 통해 수집된 데이터는 쿠키를 통해 사용자를 특정함으로써 사용자 맞춤형 광고를 표시하기 위해 사용될 수 있다.

 

이에 대처하기 위해 파이어폭스와 같은 많은 브라우저는 ETP(Enhanced Tracking Protection)라고 불리는 새로운 기능을 통해 일반적인 서드파티 추적 쿠키를 차단하기 시작했다. 이를 통해 가장 일반적으로 식별되는 3000개의 트래커로부터 사용자를 보호할 수 있지만, 그 보호는 완전하고 최신 목록에 의존한다.

 

따라서 브라우저는 최종적으로 서드파티 쿠키를 삭제할 것을 계획하고 있다. 파이어폭스는 스테이트 파티셔닝을 구현하고 있으며, 그 결과 모든 서드 파티 쿠키에 모든 웹 사이트용 개별 컨테이너가 할당된다. 그 구조에 대해서는, 'Introducing State Partitioning'에서 자세하게 읽을 수 있다.

 

스테이트 파티셔닝 같은 것은 트래킹과는 별도로 서드 파티의 Cookie의 정당한 사용 케이스를 망가뜨린다고 생각할지도 모른다. 맞다. 그래서 브라우저는 Storage Access라는 새로운 API를 개발 중이다. 이에 따라, 서드 파티의 컨텍스트는 사용자에게 허가를 요청함으로써 퍼스트 파티의 스토리지 액세스를 요구할 수 있게 된다. 이로써 서비스는 퍼스트 파티 상태에 파티션 없이 접근할 수 있게 된다.

 

결론

이 글을 통해 자바스크립트 쿠키에 대해 뭔가 새로운 것을 알게 되고 그것들이 어떻게 기능한지, 서버와 클라이언트에서 접ㅇ근해서 변경할 수 있는지, 그리고 마지막으로 쿠키의 다양한 속성을 통해 브라우저에서의 표시와 수명을 어떻게 제어할 수 있는지에 대해 쉽게 설명할 수 있기를 바란다.

 

* 번역 : A JavaScript developer’s guide to browser cookies

반응형