세션과 쿠키, 그리고 JWT

jaemin·2021년 8월 9일
1

웹의 기본, 세션과 쿠키 그리고 JWT에 대해서...

📌 HTTP 요청

현재 모바일이나 웹 서비스에서 가장 많이 사용하고 있는 통신은 HTTP 통신입니다. HTTP 프로토콜 환경에서 서버는 클라이언트가 누구인지 확인해야 합니다. HTTP 프로토콜은 connectionless하고 stateless한 특징이 있기 때문입니다.

  • Connectionless
    클라이언트가 요청을 한 후 응답을 받으면 연결을 끊어버리는 특성
  • Stateless
    통신이 끝나면 상태를 유지하지 않는 특성
    연결이 끊는 순간 클라이언트와 서버의 통신이 끝나며 상태 정보는 유지하지 않는 특성이 있다.

이러한 http 통신의 특징 때문에 요청하는 대상이 누군지 구별할 필요가 있습니다. 그런데, 사용자 계정정보를 요청에 담아보내는 방식은 보안에 굉장히 취약합니다. 따라서 나온 방식이 세션/쿠키 입니다.

📌 세션과 쿠키

순서를 살펴보면 다음과 같습니다.

  1. 사용자가 로그인합니다.
  2. 서버에서는 계정정보를 읽어 사용자를 확인한 후, 사용자의 고유한 ID값을 부여하여 세션 저장소에(주로 Redis를 사용) 저장한 후, 이와 연결되는 세션 ID를 발급받습니다.
  3. 사용자는 서버에서 해당 세션 ID를 받아 쿠키에 저장한 후, 인증이 필요한 요청마다 쿠키를 헤더에 실어 보냅니다.
  4. 서버에서는 쿠키를 받아 세션 저장소에서 대조를 한 후, 대응되는 정보를 가져옵니다.
  5. 인증이 완료되고 서버는 요청에 맞는 데이터를 보내줍니다.

세션 쿠키 방식은 기본적으로 세션 저장소를 필요로 합니다.(주로 Redis 사용) 세션 저장소는 로그인을 했을 때 사용자의 정보를 저장하고 열쇠가 되는 세션 ID값을 만듭니다. 그리고 HTTP 헤더에 세션 id를 실어 사용자에게 보냅니다. 그러면 사용자는 세션 id를 쿠키로 보관하고 있다가 인증이 필요한 요청을 보낼 때 쿠키와 함께 보냅니다. 웹 서버에서는 세션 저장소에서 세션 id를 받고 저장되어 있는 정보와 매칭시켜 인증을 완료합니다.

※ 세션과 쿠키의 차이
세션 : 서버에서 가지고 있는 정보
쿠키 : 사용자에게 발급된 세션 id를 저장하기 위한 공간


※※ 쿠키만으로 인증을 사용한다는 말은 서버의 자원을 사용하지 않는다는 말입니다. 이는 클라이언트가 인증 정보를 책임지게 되는건데, 보안에 매우매우 취약합니다. 따라서 보안과는 상관없는 장바구니나 자동로그인 설정 같은 경우에 사용됩니다.
결과적으로 인증의 책임을 서버가 지게 하기 위해서 세션을 사용합니다. (서버를 해킹하는게 더 어렵기 때문입니다.) 사용자는 쿠키를 이용하고, 서버에서는 쿠키를 받아 세션의 정보를 접근하는 방식으로 인증합니다.

세션/쿠키 방식의 장점

  1. 보안 측면에서 안정적입니다.

세션/쿠키 방식은 기본적으로 쿠키를 매개로 인증을 거칩니다. 쿠키에 담긴 세션 id는 세션 저장소에 저장되어 있는 사용자 정보를 얻기 위한 열쇠입니다. 따라서 쿠키가 담긴 HTTP 요청이 노출되더라도 쿠키 자체는 유의미한 값을 가지고 있지 않습니다. 중요한 정보들은 서버 세션에 있기 때문입니다.

  1. 서버 자원에 접근하기 용이합니다.

각각의 사용자는 고유한 id를 발급받게 됩니다. 따라서 서버에서는 쿠키 값을 받았을 때 회원정보를 확인할 필요없이 어떤 회원인지 알 수 있어 서버의 자원에 접근하기 쉽습니다.

세션/쿠키 방식의 단점

  1. 세션 하이재킹 공격에 취약

쿠키는 의미있는 정보가 없기 때문에 안전합니다. 그러나, 만약 A 사용자의 쿠키를 해커가 가로채서 HTTP 요청을 보내면 서버의 세션 저장소에서는 A 사용자의 정보를 뿌려줄 수 있습니다. (세션 하이재킹 공격)

이는 HTTPS를 사용해 요청 자체를 탈취해도 안의 정보를 읽기 힘들게 하거나, 세션에 유효시간을 넣어주는 방식으로 해결할 수 있습니다.

  1. 서버 부하⬆️

서버에서 세션 저장소를 사용합니다. 따라서 서버에 추가적인 저장공간을 필요로 하게 되고 자연스럽게 서버 부하도 높아질 것입니다.

📌 JWT 토큰 기반 인증

JWT 토큰 구성 요소

JWT는 세션/쿠키와 함께 많이 쓰이는 인증 방식입니다. JWT는 인증에 필요한 정보들을 암호화시킨 토큰을 말합니다. 세션/쿠키 방식과 유사하게 사용자는 Access Token을 HTTP 헤더에 실어 서버에 보냅니다.

토큰을 만들기 위해서 Header, Payload, Verify Signature가 필요합니다. 해커가 토큰을 조작하여 A 사용자의 데이터를 훔쳐보고 싶다고 가정하겠습니다. 그래서 payload에 있던 A의 ID를 인코딩한 후 토큰을 서버로 보냅니다. 그러면 서버가 검사했을 때 payload는 A 사용자 정보지만 verify signature는 해커의 payload를 기반으로 암호화되었기 때문에 유효하지 않는 토큰으로 간주됩니다. 따라서 해커는 SECRET KEY를 알지 못하는 이상 토큰을 조작할 수 없습니다.

JWT 인증 방식

  1. 사용자가 로그인을 합니다.
  2. 서버에서는 계정정보를 읽어 사용자를 확인한 후, 사용자의 고유한 ID값을 부여한 후, 기타 정보와 함께 Payload에 넣습니다.
  3. JWT 토큰의 유효기간을 설정합니다.
  4. 암호화할 SECRET KEY를 이용해 Access Token 발급합니다.
  5. 사용자는 Access Token을 받아 저장한 후, 인증이 필요한 요청마다 토큰을 헤더에 실어 보냅니다.
  6. 서버에서는 해당 토큰의 Verify Signature를 SECRET KEY로 복호화한 후, 조작 여부, 유효기간을 확인합니다.
  7. 검증이 완료된다면, payload를 디코딩하여 사용자의 ID에 맞는 데이터를 가져옵니다.

세션/쿠키 방식과 가장 큰 차이점은 세션/쿠키는 세션 저장소에 유저의 정보를 넣는 반면, JWT는 토큰 안에 유저의 정보를 넣는다는 점입니다. 클라이언트 입장에서는 HTTP 헤더에 쿠키를 실느냐, 토큰을 실느냐의 차이만 있지만, 서버 측에서는 인증을 위해 암호화를 하느냐, 별도의 저장소를 이용하느냐 하는 차이가 있습니다.

JWT 장점

  1. 추가 저장소가 필요하지 않습니다.

세션/쿠키는 별도의 저장소 관리가 필요한 반면, jwt는 발급한 후 검증만 하면 되기 때문에 추가 저장소가 필요하지 않습니다. 이는 stateless한 서버를 만드는 입장에서는 큰 장점입니다. stateless는 어떠한 별도의 저장소도 사용하지 않는, 즉 상태를 저장하지 않는 것을 의미합니다. 이는 서버를 확장하거나 유지, 보수하는데 유리합니다.

  1. 확장성이 뛰어납니다.

토큰 기반으로 하는 다른 인증 시스템에 접근이 가능합니다. 예를 들어 Facebook으로 로그인, 구글 로그인 등은 모두 토큰을 기반으로 인증을 합니다. 이에 선택적으로 이름이나 이메일 등을 받을 수 있는 권한도 받을 수 있습니다.

JWT 단점

  1. 한 번 발급한 JWT는 돌이킬 수 없습니다.

세션/쿠키의 경우 만일 쿠키가 악의적으로 이용된다면, 해당하는 세션을 지워버릴 수 있습니다. 하지만 JWT는 한 번 발급되면 유효기간이 완료될 때 까지 계속 사용이 가능합니다. 따라서 악의적인 사용자는 유효기간이 지나기 전까지 정보를 털어갈 수 있습니다.

※해결책※
기존의 Access Token의 유효기간을 짧게 하고 Refresh Token이라는 새로운 토큰을 발급합니다.

  1. Payload 정보가 제한적입니다.

Payload는 따로 암호화되지 않기 때문에 디코딩하면 누구나 정보를 확인할 수 있습니다. 따라서 유저의 중요한 정보들은 Payload에 넣을 수 없습니다.

  1. JWT 길이가 깁니다.

세션/쿠키 방식에 비해 JWT의 길이가 깁니다. 따라서 인증이 필요한 요청이 많아질수록 서버의 자원낭비가 발생하게 됩니다.

Refresh Token과 Access Token

앞서 말한 JWT의 문제점의 해결방법이었던 Refresh Token에 대해 알아보겠습니다. Access Token을 통한 인증 방식의 문제는 제 3자에게 탈취당할 수 있습니다. Access Token의 유효기간을 줄인다면 사용자는 그때마다 다시 로그인을 해야 합니다. 반대로 유효기간을 늘리면 토큰을 더 오래 사용할 수 있어 보안에 취약합니다. 이때 Refresh Token을 사용하여 해결할 수 있습니다.

Refresh Token은 Access Token과 똑같은 형태의 JWT입니다. 처음에 로그인했을 때 Access Token과 동시에 발급되는 Refresh Token은 긴 유효기간을 가지면서, Access Token이 만료됐을 때 새로 발급해주는 열쇠가 됩니다.

예를 들어, Refresh Token의 유효기간은 2주, Access Token의 유효기간을 1시간이라고 가정하겠습니다. 사용자는 1시간 동안 자유롭게 api 요청을 하다가 이후에 Access Token이 만료됩니다. 그러면 Refresh Token이 만료되기 전까지 새로 Access Token을 발급받을 수 있습니다.

Access Token은 여전히 탈취당할 수 있습니다. 그러나, 토큰이 유효한 시간이 짧기 때문에 기존보다 더 안전합니다.
Refresh Token의 유호기간이 만료되면 사용자는 다시 로그인해야 합니다. Refresh Token 역시 탈취의 위험이 있기 때문에 유효기간을 적절히 설정해야 합니다. (보통 2주)

Refresh Token을 사용한 JWT 인증 과정

  1. 사용자가 로그인합니다.
  2. 서버에서는 DB에서 회원을 조회합니다.
    3-4. 로그인이 완료되면 Access Token과 Refresh Token을 발급합니다. 이때 일반적으로 회원 DB 혹은 별도의 저장소에 Refresh Token을 저장해둡니다.
  3. 클라이언트는 Refresh Token을 저장소에 저장하고 Access Token을 헤더에 실어 요청을 보냅니다.
    6-7. 서버에서 Access Token을 검증하고 데이터를 응답해줍니다.

시간이 흘러 Access Token이 만료됩니다.

  1. 클라이언트는 같은 방식으로 Access Token이 담긴 헤더와 함께 요청을 보냅니다.
    10-11. 서버는 Access Token이 만료됨을 확인하고 권한없음으로 응답해줍니다.

** 반드시 서버에서 토큰 만료를 확인할 필요는 없습니다. 클라이언트에서 토큰의 Payload를 통해 유효기간을 알 수 있습니다. 따라서 프론트엔드단에서 API 요청을 보내기 전에 토큰이 만료됐다는 것을 알고 재발급 요청을 할 수도 있습니다.

  1. 클라이언트는 Refresh Token과 Access Token을 함께 서버로 보냅니다.
  2. 서버는 받은 Access Token이 조작되지 않았는 지 확인하고 Refresh Token과 사용자의 DB에 저장되어 있던 Refresh Token을 비교합니다. 토큰이 동일하고 유효기간이 지나지 않았다면 새로운 Access Token을 발급해줍니다.
  3. 서버는 새로운 Access Token을 헤더에 담아 응답을 해줍니다.

Refresh Token의 장점과 단점

장점

기존의 Access Token만 사용할 때보다 안전합니다.

단점

  1. 구현이 복잡합니다. 검증 프로세스가 길어져 구현이 조금 더 힘들어졌습니다.

  2. Access Token이 만료될 때마다 새롭게 발급하는 과정에서 생기는 HTTP 요청 횟수가 많아졌습니다. 이는 서버의 자원 낭비로 귀결됩니다.

🗒 Reference

profile
프론트엔드 개발자가 되기 위해 공부 중입니다.

1개의 댓글

comment-user-thumbnail
2021년 8월 14일

오.. 찾고 있던 내용인데 정리가 너무 잘 되어있네요 감사합니다!

답글 달기