[Backend] Cookie / Session

KIM DA MI·2023년 5월 2일
0

Backend

목록 보기
1/3
post-thumbnail

1. Cookie


  • 쿠키는 서버에서 클라이언트에 영속성 있는 데이터를 저장하는 방법이다.
    서버는 클라이언트의 쿠키를 이용하여 데이터를 가져올 수 있다.
  • 그러므로 쿠키를 이용하는 것은 단순히 서버에서 클라이언트에 쿠키를 전송하는 것만 의미하지 않고 클라이언트에서 서버로 쿠키를 다시 전송하는 것도 포함된다.

쿠키의 특징

서버가 클라이언트에 특정한 데이터를 저장할 수 있다.

  • 서버는 쿠키를 이용하여 데이터를 저장하고 이 데이터를 다시 불러와 사용할 수 있다.
    하지만 데이터를 저장한 이후 아무 때나 데이터를 가져올 수는 없다.
    데이터를 저장한 이후 특정 조건들이 만족되어야 다시 가져올 수 있기 때문이다.
  • 이런 조건들은 아래 코드처럼 http 헤더를 사용해 쿠키 옵션으로 표현할 수 있다.
    'Set-Cookie':[
                'cookie=yummy', 
                'Secure=Secure; Secure',
                'HttpOnly=HttpOnly; HttpOnly',
                'Path=Path; Path=/cookie',
                'Doamin=Domain; Domain=codestates.com'
            ]

쿠키 옵션 종류

1. Domain

  • 도메인이라는 것은 여러분들이 흔히 사용하는 www.google.com과 같은 서버에 접속할 수 있는 이름이다.
  • 쿠키 옵션에서 도메인은 포트 및 서브 도메인 정보, 세부 경로를 포함하지 않는다.
    여기서 서브 도메인이란 www 같은 도메인 앞에 추가로 작성되는 부분을 말한다.
  • 따라서 요청해야 할 URLhttp://www.localhost.com:3000/users/login이라 하면 여기에서 Domainlocalhost.com이 된다.
  • 만약 쿠키 옵션에서 도메인 정보가 존재한다면 클라이언트에서는 쿠키의 도메인 옵션과 서버의 도메인이 일치해야만 쿠키를 전송할 수 있다.
    이를 통해 naver.com에서 받은 쿠키를 google.com에 전송하는 일을 막을 수 있다.

2. Path

  • Path는 세부 경로로써 서버가 라우팅할 때 사용하는 경로를 의미한다.
  • 만약 요청해야 하는 URLhttp://www.localhost.com:3000/users/login인 경우라면 여기에서 Path, 즉 세부 경로는 /users/login이 된다.
    이를 명시하지 않으면 기본적으로 /으로 설정되어 있다.
  • Path 옵션의 특징은 설정된 경로를 포함하는 하위 경로로 요청을 하더라도 쿠키를 서버에 전송할 수 있다.
    Path/users로 설정되어 있고, 요청하는 세부 경로가 /users/codestates인 경우라면 쿠키 전송이 가능하다.
  • 하지만 /posts/codestates로 전송되는 요청은 Path 옵션(/users)을 만족하지 못하기 때문에 서버로 쿠키를 전송할 수 없다.

3. MaxAge or Expires

  • 쿠키의 유효한 기간을 정하는 옵션이다.
    만약 쿠키가 영원히 남아있다면 그만큼 탈취되기도 쉬워지기 때문에 이러한 유효기간을 설정하는 것이 보안 측면에서 중요하다.
  • MaxAge는 쿠키가 유효한 시간을 초 단위로 설정하는 옵션이다.
    마치 쿠키에게 시한부 옵션을 주는 것과 비슷하다고 볼 수 있다.
  • ExpiresMaxAge와 비슷하지만 언제까지 쿠키가 유효한지 심판의 날을 지정할 수 있다.
    이때 옵션의 값은 클라이언트의 시간을 기준으로 한다.
    이후 지정된 시간, 날짜를 초과하게 되면 쿠키는 자동으로 파괴된다.
  • 쿠키는 위 옵션의 여부에 따라 세션 쿠키(Session Cookie)와 영속성 쿠키(Persistent Cookie)로 나눠진다.
  • 세션 쿠키: MaxAge 또는 Expires 옵션이 없는 쿠키로, 브라우저가 실행 중일 때 사용할 수 있는 임시 쿠키이다. 브라우저를 종료하면 해당 쿠키는 삭제된다.
  • 영속성 쿠키: 브라우저의 종료 여부와 상관없이 MaxAge 또는 Expires에 지정된 유효시간만큼 사용가능한 쿠키이다.

4. Secure

  • 사용하는 프로토콜에 따른 쿠키의 전송 여부를 결정하는 옵션이다. 만약 Secure 옵션이 true로 설정된 경우 HTTPS를 이용하는 경우에만 쿠키를 전송할 수 있다.
  • Secure 옵션이 없다면 프로토콜에 상관없이 http://www.codestates.com 또는 https://www.codestates.com에 모두 쿠키를 전송할 수 있다.
  • 단, 도메인이 localhost인 경우에는 HTTPS가 아니어도 쿠키 전송이 가능하다.
    개발 단계에서는 localhost를 사용하는 경우가 많기 때문에 생긴 예외이다.

5. HttpOnly

  • 자바스크립트로 브라우저의 쿠키에 접근이 가능한지 여부를 결정한다.
    만약 해당 옵션이 true로 설정된 경우, 자바스크립트로 쿠키에 접근이 불가하다.
  • 옵션을 명시하지 않는 경우에는 기본적으로 false로 지정된다.
    만약 이 옵션이 false인 경우 document.cookie를 이용해 자바스크립트로 쿠키에 접근할 수 있으므로 쿠키가 탈취될 위험이 있다.

6. SameSite

  • Cross-Site 요청을 받은 경우, 요청에서 사용한 메서드(e.g. GET, POST, PUT, PATCH …)와 해당 옵션의 조합을 기준으로 서버의 쿠키 전송 여부를 결정하게 된다.
    이때, Cross-OriginCross-Site를 혼동하지 않도록 주의해야 한다.

    • Cross-Origin : 서버의 도메인, 프로토콜, 포트 중 하나라도 다른 경우 Cross-Origin으로 구분됩니다.
      • http://codestates.com vs https://codestates.com ⇒ 프로토콜이 다르므로 Cross-Origin이다.
      • https://codestates.com:443 vs https://codestates.com ⇒ https의 기본 포트는 443입니다. 따라서 도메인, 프로토콜, 포트가 모두 같은 Same-Origin이다.
    • Cross-Site : eTLD+1이 다른 경우 Cross-Site로 구분된다.
      여기서 eTLD+1 이란, .com, .org과 같이 도메인의 가장 마지막 부분을 TLD(Top Level Domain, 최상위 도메인)라고 하는데, 이 최상위 도메인의 바로 왼쪽의 하위 레벨 도메인을 합한 것을 eTLD+1 이라고 한다.
      참고로, 요즘 자주 볼 수 있는 .io의 경우 바로 왼쪽의 주소를 하나 더 합한 것을 TLD라고 판단한다.
      • http://codestates.com vs https://codestates.com ⇒ 두 주소 모두 TLD는 .com, eTLD+1은 codestates.com으로 같으므로 Same-Site이다.
      • https://code.github.io vs https://states.github.io ⇒ 두 주소 모두 TLD가 github.io로 같지만, eTLD+1은 각각 code.github.io, states.github.io로 다르므로 Cross-Site이다.
  • SameSite 옵션에서 사용할 수 있는 속성은 다음과 같다.

    • Lax : Cross-Site 요청이라면 GET 메서드에 대해서만 쿠키를 전송할 수 있다.
    • Strict : 단어 그대로 가장 엄격한 옵션으로, Cross-Site가 아닌 Same-Site인 경우에만 쿠키를 전송할 수 있다.
    • None : Cross-Site에 대해 가장 관대한 옵션으로 항상 쿠키를 보내줄 수 있다.
      다만 쿠키 옵션 중 Secure 옵션이 필요하다.

  • 서버에서 이러한 옵션들을 지정한 다음 서버에서 클라이언트로 쿠키를 처음 전송하게 된다면 헤더에 Set-Cookie라는 프로퍼티로 쿠키를 담아 전송한다.
  • 이후 클라이언트에서 서버에게 쿠키를 전송해야 한다면 클라이언트는 헤더에 Cookie라는 프로퍼티에 쿠키를 담아 서버에 쿠키를 전송하게 된다.

쿠키를 이용한 상태 유지

  • 이러한 쿠키의 특성을 이용하여 서버는 클라이언트에 인증정보를 담은 쿠키를 전송하고, 클라이언트는 전달받은 쿠키를 서버에 요청과 함께 전송하여 Stateless한 인터넷 연결을 Stateful하게 유지할 수 있다.
  • 하지만 기본적으로 쿠키는 오랜 시간 동안 유지될 수 있고, HttpOnly 옵션을 사용하지 않았다면 자바스크립트를 이용해서 쿠키에 접근할 수 있기 때문에 쿠키에 민감한 정보를 담는 것은 위험하다.
  • 이런 인증정보를 이용해 공격자가 유저인척 서버에 요청을 보낸다면 서버는 누가 요청을 보낸 건지 의심하지 않고 이를 인증된 유저의 요청으로 취급하게 된다.
    이때 개인정보와 같은 민감한 정보를 공격자가 탈취한다면 2차 피해가 일어날 수 있다.

참고 자료

MDN - Set-Cookie Attributes



2. Session


  • 서버가 Client에 유일하고 암호화된 ID를 부여
  • 중요 데이터는 서버에서 관리

세션기반 인증 (Session-based Authentication)

로그인

  • 로그인을 통해 사용자의 인증 정보를 저장하고, 그 후 인증된 사용자가 어떤 식으로 웹사이트를 이용하는지 간단하게 알아보자.
  • 사용자가 웹사이트에서 아이디 및 비밀번호를 이용해서 로그인을 시도하면(그림 ①), 과연 어떤 일이 벌어질까?
    ➡ 사용자가 만일 정확한 아이디와 비밀번호를 입력했다면, 서버는 인증(Authentication)에 성공했다고 판단할 것이다.

  • 그렇다면, 다음번에 인증을 필요로 하는 작업(e.g. 그림에서와 같이, 장바구니에 물품 추가)을 요청할 경우에 한번 더 로그인 과정을 거쳐야 할까?
    ➡ 아니다. 서버가 "해당 유저는 인증에 성공했음"을 알고 있다면, 유저가 매번 로그인할 필요가 없을 것이다.

  • 즉, 인증에 따라 리소스의 접근 권한(Authorization)이 달라진다.
    이때 서버와 클라이언트에 각각 필요한 것은 다음과 같다.
    • 서버 : 사용자가 인증에 성공했음을 알고 있어야 한다.
    • 클라이언트 : 인증 성공을 증명할 수단을 갖고 있어야 한다.

  • 여기서 몇 가지 용어가 등장한다.

    • 사용자가 인증에 성공한 상태는 세션이라고 부른다.

      • 서버는 일종의 저장소에 세션을 저장한다. (그림 ②)
        주로 in-memory(자바스크립트 객체를 생각하면 된다), 또는 세션 스토어(redis 등과 같은 트랜잭션이 빠른 DB)에 저장한다.
    • 세션이 만들어지면, 각 세션을 구분할 수 있는 세션 아이디도 만들어지는데(그림 ③),
      보통 클라이언트에 세션 성공을 증명할 수단으로써 세션 아이디를 전달한다. (그림 ④)

  • 이때 웹사이트에서 로그인을 유지하기 위한 수단으로 쿠키를 사용한다.
    쿠키에는 서버에서 발급한 세션 아이디를 저장한다.
  • 쿠키를 통해 유효한 세션 아이디가 서버에 전달되고, (그림 ⑤) 세션 스토어에 해당 세션이 존재한다면 (그림 ⑥) 서버는 해당 요청에 접근 가능하다고 판단한다. (그림에서 7,8번)
  • 하지만 쿠키에 세션 아이디 정보가 없는 경우, 서버는 해당 요청이 인증되지 않았음을 알려준다.

로그아웃

  • 그렇다면, 로그아웃은 어떻게 구현해야 할까?
    세션 아이디가 담긴 쿠키는 클라이언트에 저장되어 있으며, 서버는 세션을 저장하고 있다.
    그리고 서버는 그저 세션 아이디로만 인증 여부를 판단한다.

    ⚠ 주의 : 쿠키는 세션 아이디, 즉 인증 성공에 대한 증명을 갖고 있으므로, 탈취될 경우 서버는 해당 요청이 인증된 사용자의 요청이라고 판단한다. 이것이, 우리가 공공 PC에서 로그아웃해야 하는 이유이다.

  • 그러므로 로그아웃은 다음 두 가지 작업을 해야 한다.

    • 서버: 세션 정보를 삭제해야 한다.
    • 클라이언트: 쿠키를 변경하거나 삭제해야 한다.
  • 클라이언트에서 세션 정보를 없애기 위해서는 res.cookie로 쿠키의 값을 무효한 값으로 변경하거나, res.clearCookie로 쿠키를 삭제해 버리면 된다.

express-session

  • Node.js에는 이런 세션을 대신 관리해 주는 express-session이라는 모듈이 존재한다.
  • express-session은 세션을 위한 미들웨어로, express 서버에서 쉽게 세션을 위한 공간을 다룰 수 있도록 만들어준다.

    const express = require('express');
    const session = require('express-session');
    
    const app = express();
    
    app.use(
      session({
        secret: '@codestates',
        resave: false,
        saveUninitialized: true,
        cookie: {
          domain: 'localhost',
          path: '/',
          maxAge: 24 * 6 * 60 * 10000,
          sameSite: 'none',
          httpOnly: false,
          secure: true,
        },
      })
    );
  • express-session를 사용해 위와 같이 세션의 옵션을 지정할 수 있다.
  • 언뜻 보면 쿠키 옵션과 비슷해 보이지만 세션의 경우
    secret 옵션의 비밀키를 이용해 암호화해 세션 id라는 것을 생성한다.
    그리고 이것을 클라이언트에게 쿠키로 전송한다.
  • 쿠키로 전송된 세션 id는 이에 종속되는 고유한 세션 객체를 가지며 이는 서버에 저장된다.
    이때 세션 객체는 유저별로 독립적으로 생성된 객체이므로 유저별로 각각 다른 데이터를 저장할 수 있다.
  • 따라서 클라이언트에 유저의 개인정보를 담지 않고도 서버가 클라이언트의 세션 id를 이용해 유저의 인증여부를 판단할 수 있다.
  • 세션 객체는 req.session으로 접근할 수 있으며 앞서 말했듯 이를 통해 세션에 임의의 데이터를 저장하거나 불러올 수 있다.

참고 자료



3. Cookie & Session


설명접속 상태 저장 경로장점단점
Cookie쿠키는 그저
http의 stateless한 것을
보완해주는 도구
클라이언트서버에 부담을 덜어줌쿠키 그 자체는 인증이 아님
Session접속 상태를 서버가 가짐 (stateful)
접속 상태와 권한 부여를 위해
세션 아이디를 쿠키로 전송
서버신뢰할 수 있는 유저인지
서버에서 추가로 확인 가능
하나의 서버에서만
접속 상태를 가지므로
분산에 불리

0개의 댓글