IT Study/CS

[Back] 계속 헷갈리는 토큰 vs 쿠키 vs 세션

짹짹체유 2023. 11. 14. 15:15

 

 

프로젝트에서는 가장 처음엔 토큰을 생성해서 '로그인을 유지' 시키기 위해 브라우저의 로컬 스토리지에 저장을 했다.

그러나 최종 프로젝트에서는 쿠키와 토큰을 사용했다

 

쿠키로 바꾼 이유는 ?

로컬 스토리지는 브라우저를 종료해도 데이터가 남아있다는 특성을 지니기 때문에 보안에서 취약하다.

쿠키와 토큰을 사용함으로써 만료 기간을 설정해 보안성을 높이고자 했다.

 

구현한 Flow를 요악하면, 첫 로그인 시 JWT 토큰을 쿠키에 담아서 클라이언트로 보내고

유저가 페이지를 접속할 때마다 클라이언트는 헤더에 쿠키정보를 담아서 서버로 보내면

서버는 JWT 토큰을 검증하고 유효하다면 요청값을 보내주는 방식으로 인증 과정을 거쳤다.

모든 데이터를 서버에서 저장해야한다는 부담과 (물론 소규모 프로젝트라 서버거 터질일은 절대절대 없겠지만)

HTTP는 stateless하기에 쿠키를 사용하고 안전성을 높이기 위해 JWT토큰을 사용하여 데이터 정보를 클라이언트에서 저장하도록 구현했다.

 

기본적인 사용 목적: 서버가 클라이언트를 인증해야함

 

 

쿠키, 세션, 토큰의 등장 배경

쿠키가 등장한 당시에는 하드웨어 사양이 좋지 않아 웹서버에 많은 데이터를 저장할 수 없어서 클라이언트에서 저장했다. 다만 브라우저에 저장되기에 쉽게 노출되어 개인정보 유출에 취약하다는 단점이 존재했다. 시간이 지남에 따라 하드웨어 비용이 감소해 서버 성능이 증가해서 서버에 데이터를 저장하는 방식인 세션이 등장했다. 세션은 쿠키의 진화형으로 서버에 저장함으로써 안전성이 더해졌다. 또한 데이터 크기에 제한이 있던 쿠키와 다르게 세션은 서버 성능에 따라 데이터 저장이 자유로워졌다. 이때부터 데이터를 서버가 관리하는 형태가 되었다. 그러나 인터넷 사용이 보편화되면서 유저 접속이 많은 서버는 많은 데이터들을 모두 저장해야해서 많은 양의 메모리를 소모하고 속도도 느려지며 서버가 과부화되기도 한다.

쿠키와 세션은 각기 장단점을 가지고 있다. 이러한 단점들을 보완하기 위해 등장한 비교적 최근의 방식은 토큰 기반의 인증방식이다.

 

 

< 기술적인 측면에서 요약 >

상태정보를 서버에 저장하면 stateful(상태유지), 서버에 저장하지 않으면 stateless(무상태)

 

HTTP는 Stateless 프로토콜로 기본적으로 상태정보를 기억하지 못한다. 즉 새로고침을 하면 모든 정보들이 날아간다.

이로 인해 클라이언트는 서버에 요청을 보내면서 응답받은 값들로 페이지를 로드한다.

 

세션은 서버에 저장( stateful )되며, 쿠키는 클라이언트에 저장( stateless )된다.

세션을 사용하면 안전성은 높겠지만 로그인과 같은 상태들은 stateful한 상태를 사용해야 한다.

stateless 특징을 유지하면서도 로그인 상태 유지가 가능한 기술 -> JWT 토큰

 


1️⃣ 쿠키

- '키 : 값'의 문자열 형식

- 유저가 사이트를 방문하면 서버를 통해 클라이언트의 브라우저에 작은 기록 정보 파일이 저장

- 유저마다 브라우저에 정보를 저장해서 고유 정보 식별이 가능함

- 웹 브라우저마다 쿠키의 지원형태가 달라 브라우저간에는 공유 불가

       -> 크롬에서 접속하고 쿠키를 받았다고 해서 웨일에서 같은 쿠키로 접속 x


인증 방식

1. 브라우저에서 요청을 보냄

2. 서버에서 요청 받음 & 쿠키 생성  & 응답

  • 쿠키를 생성하고 응답 헤더의 set-cookie에 담아서 보냄

https://seob.dev/posts/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%BF%A0%ED%82%A4%EC%99%80-SameSite-%EC%86%8D%EC%84%B1/

 

 

프로젝트에서는 user/login url로 요청이 오면 유저 정보가 있는지 판단한 후,
유저 정보가 있다면 JWT을 생성해서 쿠키에 담아서 전송
userAuthRouter.post("/user/login", deleted_checked, request_checked, async function (req, res, next) {
  try {
    const { email, password } = req.body;
    // 유저 정보 확인
    const { errorMessage, accessToken, loginUser } = await userAuthService.getUser({ email, password });
    if (errorMessage) {
      throw new NotFoundError(errorMessage);
    }
    
    // 쿠키 생성 및 전송
    res.cookie("user_cookie", accessToken, {
      path: "/", // 쿠키 저장 경로
      httpOnly: true, // 클라이언트에서 쿠키 조작 불가
      sameSite: "lax",
      maxAge: 3 * 60 * 60 * 1000, // 쿠키의 유효기간 (3시간)
    });

    const { id, name, description, userImgUrl } = loginUser;
    res.status(200).send({ id, email, name, description, userImgUrl, message: "로그인에 성공했습니다." });

  } catch (error) {
    next(error);
  }
});

*deleted_checked: 탈퇴한 회원인지 확인하는 미들웨어

*request_checked: 요청값이 있는지 확인하는 미들웨어

 

 

※ 쿠키 생성 옵션

  • path: 쿠키의 저장 경로. '/'는 모든 경로에서 접근 가능
    • ex. '/auth'로 설정 -> /auth나 /auth/login 등의 하위 주소에서 쿠키로 접근이 가능
  • httpOnly: 브라우저에서 쿠키 값에 접근 가능 여부
    • true: 접근 불가능
      • 클라이언트가 접근이 안돼서 쿠키 조작 불가
      • 자바스크립트에 있는 쿠키를 접근하려고 하는 것: XSS 공격
      • XSS 공격을 막음으로써 보안에 유리
    • default: false
  • secure
    • HTTP의 해킹을 예방하고자 보안이 강화된 HTTPS 프로토콜로 데이터 암호화.
    • true: HTTPS 통신에만 쿠키 전달 가능
    • default: false
프로젝트에서는 서버와 클라이언트 모두 HTTP 였기에 secure 옵션을 설정하지 않음
  • sameSite
    • None: 크로스사이트 요청의 경우에도 항상 전송 가능. 서드 파티 쿠키로도 전송 가능
    • Strict: 가장 보수적. 크로스사이트 요청에는 전송 불가. 서드 파티 쿠키는 전송이 안되며 퍼스트 파티 쿠키만 전송 가능
    • Lax: 서드 파티 쿠키는 전송되지 않으나 예외적인 요청에는 전송.
      • 크롬은 기본적으로 sameSite의 기본값을 Lax로 설
보통 sameSite가 None이라면 보안상 취약해서 HTTPS 통신을 사용하고 secure를 ture로 설정한다.
  • maxAge: 쿠키의 유효시간. 단위: 초(second)
  • expires: 쿠키의 유효시간. 형식: HTTP-date timestamp. maxAge와 함께 사용하면 maxAge가 적용
  • domain: 도메인 설정 (default: 현재 문서 url)

 

3. 브라우저에 쿠키 저장 & 요청 보낼 때마다 헤더에 담아 전송

  • 서버로 요청을 보낼 때 헤더에 cookie 담아서 같이 전송

 

  • 서버에서는 헤더에 있는 cookie 값 추출
async function login_required(req, res, next) {
  // 쿠키 헤더 값을 가져옴
  const cookies = req.headers["cookie"] || '';
  const cookieArray = cookies.split(';');

 

4. 서버에서는 쿠키값으로 사용자 인증


 

쿠키 종류

1) 세션 쿠키

- 임시 쿠키로 브라우저를 닫는 순간 삭제

- Expires나 Max-Age 파라미터가 없다면 세션 쿠키 

 

2) 지속 쿠키

- 세션 쿠키와 다르게 삭제되지 않고 길게 유지 가능.

- 디스크에 저장되며 브라우저를 닫거나 컴퓨터를 재시작해도 남아있음. 

- Expires나 Max-Age 파라미터로 설정

 

 

+ 서드 파티 쿠키

 

 

2️⃣ 세션

- 일정기간 동안 클라이언트로부터 들어오는 요청을 하나의 상태로 보고 그 상태를 일정하게 유지시키는 기술

- 데이터를 서버측에 저장하고 관리한다.

- 서버로부터 발급 받은 세션 ID를 쿠키에 넣어 클라이언트에 전송 한다.

- 세션ID를 통해서 클라이언트를 인증하고 브라우저를 종료할 때까지 세션을 유지한다.

- 보안 측면에서 쿠키보다 비교적 우수하다

- 서버 용량의 허용 범위 내에선 저장 데이터에 제한이 없다.

 

쿠키를 통해 세션ID가 저장 및 관리되기에 쿠키와 작동 방식은 유사하나, 세션은 서버가 발급한 세션ID로 통신을 하며 서버에서 클라이언트를 인증한다는 차이가 있다. 세션ID가 쿠키를 통해 클라이언트에 저장이 된다고 해도, 클라이언트의 정보 자체는 서버에 저장이 되어있어서 비교적 안전하다.

 

인증 방식

1. 브라우저에서 요청을 보냄

2. 서버에서 요청 받음 & 쿠키 생성  & 세션ID 생성 & 응답

  • 쿠키를 생성하고 세션ID를 생성해서 클라이언트에게 전송

3. 브라우저에 쿠키 저장 & 요청 보낼 때마다 헤더에 담아 전송

4. 서버에서는 세션ID로 사용자 인증

 


세션의 단점

- 쿠키에 비해 느리다

- 서버의 자원을 사용해서 속도 저하와 과부화 우려가 있다

- 해킹 위험성도 여전하다

 


3️⃣ 토큰

JWT토큰

- JSON Web Token으로 JSON 형식의 개방형 표준 규격이다.

- 데이터는 디지털 서명을 통해 확인되고 HTTP를 통해 전송되는 경우 암호화된다.

- 클라이언트가 암호화된 로그인 정보를 지니고 있다가 서버에 통신할 때 넘겨줌으로써 유효성 검증을 통해 접근 가능성 파악

 

※ 구성요소

  • Header: 토큰 유형, 서명 알고리즘 명시
  • Payload: 사용자의 인증/인가 정보

-> Header와 Payload는 Json이 디코딩 되어있을 뿐 암호화되어있지 않기 때문에 JWT를 가지고 디코딩을 하면 누구든지 그 값을 알 수 있다. 따라서 민감한 정보는 담지 않는 것이 좋다.암호화는 민감한 정보를 막을 때 필요한 것으로 암호화 자체로도 많은 리소스를 사용해서 필요한 경우에만 사용해야 한다. 따라서 Header와 Payload에는 민감하지 않은 기본 스펙 등의 정보들만 담고 굳이 암호화하지 않아도 되는 것이다.

  • Signature: 헤더와 페이로드가 비밀키로 서명
    • 암호화 되어있는 상태로, 서버에 있는 개인키로만 암호화를 풀 수 있어서 다른 클라이언트는 임의로 복호화가 불가능하다.

=> 각 구성요소들은 점(.)으로 구분

https://brunch.co.kr/@jinyoungchoi95/1

 

 

 

토큰의 장점

- 세션과 달리 클라이언트의 상태를 서버가 저장하지 않아도 

- 개인키 암호화를 통해 막아 보안성 증가

- 토큰 자체가 인증 정보로 세션 저장소와 같은 별도의 인증 저장소가 필수적이지 않음

 

 

 

참고자료

< 쿠키와 세션 >

https://pomo0703.tistory.com/207

https://nsinc.tistory.com/121

https://seob.dev/posts/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%BF%A0%ED%82%A4%EC%99%80-SameSite-%EC%86%8D%EC%84%B1/

https://velog.io/@sms8377/Network-%EC%BF%A0%ED%82%A4-%EC%98%B5%EC%85%98%EC%9D%98-%EC%97%AD%ED%95%A0

https://jake-seo-dev.tistory.com/377

https://blog.naver.com/PostView.nhn?blogId=dlaxodud2388&logNo=221917137726

https://hec-ker.tistory.com/368

https://dololak.tistory.com/535

https://kyun2da.github.io/2020/12/28/cookieSession/

 

< 토큰 >

https://brunch.co.kr/@jinyoungchoi95/1

https://www.okta.com/kr/identity-101/what-is-token-based-authentication/

 

 

 

반응형