[브라우저] 쿠키란 무엇일까

ChoiYongHyeun·2024년 3월 18일
0

브라우저

목록 보기
6/16

대부분의 글은 HTTP Cookies Crash Course 를 보고 작성했습니다

쿠키가 뭘까

HTTPStateless

기본적으로 웹 통신에 사용하는 프로토콜인 HTTPstateless 를 지향한다.

단순히 클라이언트와 서버 간 통신에서

어떤 정보를 , 어디로 전송할지에 대한 정보만 가지고 통신을 주고 받으며

단순히 요청 성공 , 요청 실패 등에 대한 응답과 함께 정보를 주고 받는다.

이렇게 요청 시 마다 클라이언트가 어떤 상태였는지와 상관 없이

그저 요청에 필요한 정보만 header 에 담아 요청을 주고 받는 단순한 구조 덕분에

유지보수가 쉽고 직관적인 통신이 가능했다.

하지만 우리는 종종 stateless 한 통신이 아니라 stateful 한 통신을 원할 때가 있다.

stateful 하게 통신 하고 싶은 상황

가장 먼저 생각나는 stateful 한 상황은 로그인과 관련된 상황일 것이다.

예를 들어 어떤 페이지에서 회원과 비회원에게 보이는 화면이 다르다고 가정해보자

이 때 여전히 통신을 stateless 하게 해둔다면

클라이언트는 회원에게 보이는 화면을 보기 위해서는 항상

서버와 통신을 주고 받을 때 아이디와 비밀번호를 입력해야 할 것이다.

요청을 주고 받는 순간 아이디와 비밀번호를 header 에 적어야 하기 떄문이다.

혹은 인터넷 쇼핑을 할 때 장바구니에 물품들을 모두 담은 후 미처 못담은 물품이 있어

뒤로 가기 버튼을 눌렀다가 다시 장바구니를 들어갔을 때

여태 담아뒀던 물품들이 모두 사라져있다면 사고싶은 마음이 쏙 사라질 것이다.

어떻게 나의 state 를 기억해둘 수 있을까

이를 해결하기 위해 브라우저는 사용자의 state 를 기억 할 수 있는 저장 공간들을 마련했다.

그것도 아주 귀여운 이름을 가진 쿠키저장소 라는 이름으로 말이다.

쿠키 저장소의 쿠키들은 키와 밸류 값으로 이뤄진 간단한 텍스트들이다.

브라우저마다 다르지만 쿠키는 최대 4KB 까지의 값들만을 가질 수 있으며

쿠키는 서버와 통신 할 때 , 어떤 도메인 (어떤 페이지) 에서 통신을 하느냐에 따라

요청의 header에 담겨 보내질수 있다.

쿠키는 서버가 클라이언트와 요청을 주고 받을 때 필요한 정보들을 브라우저 단에 저장해둔 문자열이다.

벨로그에 저장된 나의 쿠키 정보를 보자

벨로그에서 테마를 다크 모드로 선택했을 때

내 브라우저 쿠키 저장소에는 theme = dark 라는 형태로 쿠키가 저장되어 있으며

해당 쿠키는 벨로그와 통신을 주고 받을 때 request headerCookie 부분에서 서버 측으로 보내진다.

쿠키란 무엇일까 정리

쿠키란 클라이언트와 서버와 통신 할 때 필요한 정보들을 브라우저 메모리 단에 저장해둔 문자열이다.

브라우저에 저장된 쿠키들은 설정에 따라 서버와 통신 할 때 request header 에 담겨 전송되며
서버는 Cookie 값을 이용해 클라이언트의 상태를 파악한다.

서버 : 음 ~ 얘의 테마는 다크 모드군 , 음 ~ 얘의 장바구니 목록은 이렇군 ~


쿠키를 생성하는 방법

클라이언트

쿠키는 서버 단에서 생성해줄 수도 , 클라이언트 단에서 생성 할 수 있다.

우선 클라이언트 단에서 생성하는 방법을 살펴보자

브라우저 객체인 document.cookie 로 쿠키 값에 접근 할 수도 , 설정 할 수도 있다.

이처럼 쿠키를 설정한 후 개발자도구 -> application -> cookie 에 들어가면 현재 존재하는

쿠키 값 등을 볼 수 있다.

서버 단에서 쿠키 설정

서버 단에서 클라이언트의 브라우저에 쿠키를 설정하는 방법은

response header 에서 Set-cookie 영역에서 설정할 쿠키의 이름 , 값 , 쿠키의 옵션 등을 담아 전송해주면 된다 .

간단하게 express 를 이용해서 로그인 과정을 오늘 공부해보았던 것인데

res.cookie 를 통해 쿠키의 이름 , 값 , 쿠키의 옵션을 설정하여 클라이언트에게 전송한다.

이는 Response headerSet-Cookie 값으로 담겨 브라우저에게 전송되며

브라우저는 Response headerSet-cookie 헤더를 파싱하여

자동으로 쿠키 브라우저에 저장해준다.

브라우저야 고마워 ~!


인증과 인가

쿠키의 다양한 프로퍼티를 실습하기 위해

진행해보려 하는 토이 프로젝트의 프로토타입을 간단하게 만들어봤다.

쿠키는 다양한 용도로 많이 사용되지만 , 예시 중 하나인

사용자의 신원 정보를 통신 할 때 주고 받을 수 있도록 하는 과정인

인증과 인가를 구현해봤다.

클라이언트가 서버의 어떤 정보에 접근하기 위해선

자신이 어떤 사용자인지 확인시키는 인증이 필요하고

어떤 행위를 하기 위해서 조건이 필요하다면 , 사용자가 조건에 만족하는지 확인시키는 인가가 필요하다.

인증

로그인 Form 을 서버에 제출하면

서버 측에선 해당 아이디와 비밀 번호를 확인 후

해당 아이디와 비밀 번호를 브라우저 쿠키 저장소에 저장한다.

app.post('/login', (req, res) => {
  if (!req.body) {
    res.status(400).send({ message: 'Form 이 비어있어요' });
  }

  const { userId, password } = req.body;
  const user = db[userId];
  if (!user) {
    res.status(400).send({ message: '아이디를 확인해주세요' });
    return;
  }

  if (user['password'] !== password) {
    res.status(400).send({ message: '비밀번호를 확인해주세요' });
    return;
  }
  // 아이디와 비밀번호가 데이터베이스와 맞으면 아이디오 비밀번호를 쿠키 값으로 설정
  const cookieOption = {};
  res.cookie('userId', userId, cookieOption);
  res.cookie('password', password, cookieOption);
  res.status(200).send({ message: 'good' });
});

서버 측에서 Set-Cookie 헤더를 지정해주면

브라우저 측에서 쿠키를 설정한다.

이처럼 클라이언트의 신원을 확인하는 프로세스를 인증 이라고 한다.

서버는 클라이언트의 신원을 아이디와 비밀번호를 통해 확인했고 ,

추후 인가에 사용 될 수 있는 정보(쿠키)를 브라우저 단에 저장해주었다.

인가

이번에는 사용자가 자신의 신원정보가 들어있는 마이 페이지에 접속하려고 한다고 해보자

로그인 한 채로 마이페이지의 버튼을 눌러 접속 할 수도 있을 것이고

마이페이지의 url 에 접속 할 수도 있을 것이다.

이 때 만약 마이페이지 주소가 www.example.com/mypage/:userId 로 구성됐을 때

누군가가 :userId 에 어떤 아이디 명을 넣어도 해당 페이지에 접속이 가능하다면

신원정보가 무분별하게 유출되고 말 것이다.

이 때 클라이언트가 어떤 행위를 하려고 할 때 해당 클라이언트가 행위를 할 권한이 있는지

확인하는 것을 인가 라고 한다.

app.get('/Mypage/:userId', (req, res) => {
  const { cookies, params } = req;
  const { userId, password } = cookies;
  const requestUserId = params.userId;
  const user = db[requestUserId];

  if (userId !== requestUserId) {
    res
      .status(404)
      .send({ message: '마이페이지에서는 해당 계정에 로그인 해야 합니다' });
    return;
  }

  if (password !== user.password) {
    res.status(404).send({ message: '마이페이지 접근할 권한이 없습니다' });
    return;
  }

  res.status(200).json(user.information);
});

위 시뮬레이션 과정에서는 마이페이지에 접근하려 할 때 리퀘스트 헤더에 쿠키를 집어 넣어 서버에 요청하고

서버는 쿠키에서 제공하는 정보를 이용해 권한이 있는지를 확인한다 .

정리

클라이언트와 서버가 리소스를 주고 받을 때 , 리소스 사용자의 정보에 따라 권한을 다르게 주고 싶을 때 사용 하는 것이 인증과 인가이다.

쿠키를 이용하여 인증과 인가를 설명한다면

클라이언트가 로그인을 통해 본인을 식별 할 수 있는 정보를 서버에 제출 , 서버에서는 확인하여 자신과 통신하고 있는 클라이언트의 정보를 식별하는 것을 인증 이라고 한다.

이런 인증된 정보는 브라우저의 쿠키로 저장되어 statelessHTTP 통신에서 헤더를 통해 stateful 한 정보를 주고 받을 수 있다.

덕분에 매번 정보를 주고 받을 때 마다 로그인 정보를 입력 할 필요가 없다.

쿠키에 담겨 헤더로 전송되는 stateful 한 정보들을 가지고 , 서버 측에서 권한을 요구하는 행위를 클라이언트가 하려 할 때 권한이 있는지 확인하는 과정을 인가라고 한다.

이 때 인가 과정에서 사용되는 정보는 Cookie 헤더에 담겨있는 정보를 이용해 권한을 확인한다.


쿠키의 프로퍼티들

위에서 쿠키가 무엇인지, 쿠키가 왜 필요한지에 대한 예시로 인증과 인가에 대한 이야기를 나눴다.

 const cookieOption = {};
  res.cookie('userId', userId, cookieOption);
  res.cookie('password', password, cookieOption);

기본적으로 서버측에서는 다음처럼 쿠키의 이름 , 값 , 그리고 쿠키의 프로퍼티등 을 담은 객체와 함께

Set-Cookie 헤더를 설정한다.

이 때 사용하는 프로퍼티들을 살펴보자

브라우저 객체는 하나요 , 클라이언트가 사용하는 웹 페이지는 무진장 많을 것이다.

또 웹 페이지 별로 브라우저에 저장 시키는 쿠키는 더욱 많을 것이다.

이 때 특별한 설정이 없다면 , 브라우저는 서버와 통신을 주고 받을 때 브라우저에 저장된 쿠키들을 모두 헤더에 담아 전송한다.

내가 A 페이지에 접속하여 통신을 주고 받을 때 무진장 많은 쿠키를 헤더에 담아 전송한다고 생각해보자.

그러면 다음과 같은 문제들이 생길 것이다.

  1. 헤더의 용량이 부족하여 모든 쿠키를 전송하는게 불가능 할 수 있다.
  2. A 페이지와 상관이 없는 정보를 담고 있는 쿠키가 전송된다.

이를 해결하기 위해 쿠키의 프로퍼티 중 Cookie Scope 를 지정하는 프로퍼티들이 있다.

쿠키 스코프는 쿠키가 전송 될 수 있는 범위를 의미하고

이런 범위를 Cookie Scope Properties 를 통해 제어해줄 수 있다.

예를 들어 www.naver.com 에서 생성된 쿠키는 www.naver.com 서버와 통신 할 때만 전송 되도록 도메인 경로를 설정해 줄 수도 있고

www.naver.com/login 주소에만 전송 될 수 있도록 하위 경로를 지정해줄 수도 있다.

Domain

Domain 프로퍼티는 쿠키가 전송 될 수 있는 Domain 주소를 의미한다.

만약 설정되지 않는다면 Domain 값은 쿠키가 생성된 서버의 도메인 주소로 설정된다.

Domain 주소는 하위 도메인 주소 방향으로만 계층적으로 탐색 할 수 있다.

만약 Domainnaver.com 으로 설정된 경우 naver.com 이나 www.naver.com 으로도 전송이 가능하지만

www.naver.com 으로 설정된 경우에는 naver.com 으로는 전송이 불가능하다.

이는 보안과 관련된 의도적 설계로 , 쿠키의 범위를 제한함으로서 특정 서브 도메인의 쿠키가 다른 도메인이나 상위 도메인과 공유되지 않도록 한다.

Path

Path 는 하위 경로를 의미한다.

예를 들어 www.naver.com 까지가 도메인 주소였다면

www.naver.com/login 에서 /login 과 같은 하위 경로를 Path 라고 한다.

Path 값을 따로 설정해주지 않는다면 모든 하위 경로에 대해서 쿠키가 전송되지만

Path 값을 설정해준다면 설정된 하위 경로 이하의 모든 경로에 전송이 가능하다.

예를 들어 다음처럼 MyPage 로 이동하기 위해서는 쿠키에 설정된 아이디와 비밀번호를 전송해

인가를 받아야 할 것이다.

하지만 메뉴를 이동 할 때 아이디와 비밀번호를 전송 할 필요가 없다.

이 때 쿠키가 전송 될 Path 를 지정해줌으로서 쿠키가 조건적으로 전송이 가능하게 해줄 수 있다.

  const cookieOption = {path: '/MyPage'};
  res.cookie('userId', userId, cookieOption);
  res.cookie('password', password, cookieOption);
  res.status(200).send({ message: 'good' });

쿠키를 설정 할 때 쿠키가 전송될 경로를 /MyPage 로만 가능하게 할 경우

쿠키가 설정 될 때 Set-Cookie 헤더에 쿠키의 프로퍼티 (Path)가 지정되고 브라우저 단에서는

쿠키의 Path 가 설정된 채로 저장된다.

/MyPage request

/content

설정된 쿠키는 Path 에서 지정된 하위 경로로 접속 할 때에만 전송이 된다.

쿠키의 생명주기와 관련된 프로퍼티로는 Expires , Max-Age 가 존재한다.

쿠키는 생명주기에 따라 두 가지 쿠키로 나뉜다.

  1. 세션 쿠키 : 브라우저 메모리에 저장되며 해당 브라우저가 종료될 경우 쿠키의 정보는 사라지게 된다.
  2. 지속 쿠키 : Persistent Cookie브라우저 디스크에 저장되며 해당 브라우저가 종료되더라도 설정된 기한 전까진 제거되지 않는다.

브라우저 메모리 , 브라우저 디스크 라고 해서 브라우저가 자체적으로 메모리와 디스크를 갖는 것이 아니라

클라이언트 컴퓨터의 메모리 , 디스크 영역에 저장되는 것이다.

Expires , Max-Age 프로퍼티는 지속 쿠키를 만들 때 사용하는 프로퍼티로 , 해당 지속 쿠키가 얼마나 디스크에 저장되어 있을 기간을 정한다.

만약 두 프로퍼티를 지정해주지 않으면 기본적으로 쿠키는 세션 쿠키 형태로 저장된다.

세션쿠키와 브라우저 쿠키 모두 장단점과 활용방안이 있다.
세션 쿠키는 메모리 단에 저장되어 있기 때문에 조회가 빠르고 임시적인 데이터들을 저장해둘 수 있다.

지속 쿠키는 디스크 단에 저장되어 있기 때문에 조회가 비교적 느리지만 브라우저의 세션과 상관 없이 데이터들을 저장 할 수 있다.

브라우저의 세션은 클라이언트가 웹 브라우저를 열 때 시작되고 닫을 때 종료된다.

Expires

Expires 속성은 정확한 날짜와 시간을 지정하여 사용한다.

예를 들어 Expires=Wed, 2025년 10월 21일 07:28:00 GMT 처럼 설정하여 사용해야 한다.

const expires = new Date();
expires.setDate(expires.getDate() + 7); // 앞으로 7일 후로 설정

const cookieOption = {
    expires: expires,
};
res.cookie('userId', userId, cookieOption);

Max-Age

Max-Age 는 쿠키가 생성된 시점으로부터 밀리세컨드 단위로 쿠키가 저장될 기한을 정한다.

  const cookieOption = {
    maxAge: 7 * 24 * 60 * 60 * 1000,
  };
  res.cookie('userId', userId, cookieOption);
  res.cookie('password', password, cookieOption);
  res.status(200).send({ message: 'good' });
});

밀리세컨드 단위이기 때문에 단위를 밀리 세컨드 단위로 변환하여 사용해준다.

sameSite

sameSite 옵션은 해당 쿠키가 전송될 수 있는 컨텍스트를 제한한다.

어떻게 제한하느냐에 따라 다른 홈페이지로의 쿠키 전송을 막을 수도 , 혹은 다른 홈페이지와 쿠키를 주고 받을 수 있다.

내가 A 사이트를 개발한 개발자이고 A 사이트는 다양한 페이지들의 글을 모아서 중계하는 ㅅ사이트라고 해보자 .

이 때 A 사이트에서 해당 글에 좋아요를 누르면, 원본 글에도 좋아요가 눌린다고 해보자.

현재 상황은 A 사이트에서 로그인하여 B 사이트의 글에 좋아요 (POST)를 누른 상황이다.

  1. sameSite : None

sameSite : None 의 경우에는 쿠키가 전송될 수 있는 컨텍스트를 제한하지 않기 때문에 A 사이트에서 설정된 쿠키들도 B 사이트에 좋아요 요청이 눌릴 때 같이 전송된다.

이를 통해 B 사이트에서는 A 사이트의 쿠키를 통해 클라이언트의 정보를 확인하여 좋아요 요청을 확인하는 것이 가능하다.

  1. smaeSite : Stric

smaeSite : Stric 의 경우에는 쿠키를 동일한 홈페이지 외에는 쿠키를 제공하지 않는다. 이에 B 사이트에 좋아요 요청을 눌러도 A 사이트의 쿠키는 전송되지 않아 인가가 불가능 해진다.

하지만 A 사이트에서 제공하는 쿠키를 다른 홈페이지로 보내지 않아 보호 할 수 있다는 장점이 있다.

  1. smaeSite : Lax

sameSite : Lax 의 경우는 좀 더 유연하게 쿠키를 전송한다. 안전한 요청으로 간주되는 최상위 도메인의 GET 요청의 경우에는 쿠키를 전송하고 , 불안전한 요청인 POST 등과 같은 경우에는 쿠키를 전송하지 않는다.

좀 더 자세한 내용은 위키피디아를 참고하자 >< 갈 길이 멀어서 쿠키에 대한 글은 여기까지만 하도록 하겠다

쿠키 위키피디아

profile
빨리 가는 유일한 방법은 제대로 가는 것이다

0개의 댓글