프로젝트

Session 기반 인증과 Token 기반 인증의 차이가 뭘까?

ummchicken 2023. 2. 20. 15:49

토큰이 도대체 뭐길래. 

 

세션을 공부하니까, 토큰을 알게되었다.

 

역시 하나로 끝나지 않는 코딩의 세계.

 

 


 

✔️ 인증(Authentication)과 인가(Authorization)의 차이

 

💡 인증 (Authentication)

말 그대로 내가 누군지 인증하는 것이다.
쉽게 말하면 '로그인'이다.

클라이언트가 자기자신이라고 주장하고 있는 사용자가 맞는지를 검증하는 과정이다.
  • 로그인과 같이 사용자 또는 프로세스의 신원을 확인하는 프로세스이다.
  • 인증에 사용할 데이터와 저장되어 있던 데이터를 비교한다.
  • 이 데이터는 인증 서버에 저장되며, 가장 일반적인 인증 방법은 비밀번호를 사용하는 것이다.

 

 

 

 

💡 인가 / 권한 부여 (Authorization)

허용된 권한을 말한다.

인가는 인증 작업 이후에 행해지는 작업으로, 
인증된 사용자에 대한 자원 접근 확인 절차를 의미한다.
  • 누가 무엇을 할 수 있는지 결정하는 규칙이다.
  • 사용자가 로그인하면 각 후속 요청에 JWT가 포함되어 사용자가 해당 토큰으로 허용되는 경로, 서비스 및 리소스에 엑세스 할 수 있다.
  • JWT가 유용하게 사용되는 시나리오 중 하나이다.

 

 

 


 

✔️ HTTP의 비상태성(Stateless)

HTTP는 stateless한 특성을 가지기 때문에 각 통신 상태는 저장되지 않는다.

하지마 매번 새 페이지를 요청할 때마다 로그인을 해야 한다면, 사용이 불가능할 것이다.

 

 

 

💡 이 문제를 해결하기 위한 대표적인 도구 두 가지가 세션(Session)토큰(Token)이다.

  • 유저가 로그인을 시도할 때 서버상에서 일치하는 유저 정보를 찾았다면, 인증(Authentication) 확인의 표시로 세션이나 토큰을 발급/전달 해준다.
  • 그럼 웹 브라우저에서 해당 세션/토큰 정보를 받아 간직하고 있다가 
  • 새로운 request를 보낼 때마다 인가(Authorization)를 위해 해당 세션/토큰을 함께 보낸다.

 

 

 

💡 세션과 토큰 모두 존재 목적은 같지만, 차이점은 존재한다.

  • 가장 큰 차이점은 세션은 데이터베이스 서버에 저장된다.
  • 토큰은 클라이언트 측에서만 저장된다.

 

 

 

로그인을 통해 인증이 되면, 이제 권한을 부여해야 한다.

이런 권한을 부여하는 과정에서 사용되는 기술이 Session과 JWT이다.

 

 


 

✔️ Session

Session은 서버 기반 인증의 대표적인 예이다.

 

  1. 클라이언트가 로그인을 위해 인증 정보를 서버에 전송한다.
  2. 서버는 메모리에 사용자를 저장하고, 이 안에 로그인한 유저의 정보가 저장이 된다. 세션에 저장된 정보에는 고유의 세션ID가 부여된다.
  3. 세션 아이디를 쿠키로 전달한다.
  4. 클라이언트는 쿠키에 저장된 세션 아이디를 이용하여 요청을 보낸다.
  5. 서버는 일치하는 세션 아이디를 메모리에서 검색한 후 응답을 보내준다.
  6. 페이지가 이동할 때마다 이 작업이 반복되고, 로그인 상태가 유지되는 것이다.

 

 

즉,

  • Session 정보는 DB에 저장되며,
  • 클라이언트는 SessionID를 가지고 있다.
  • 클라이언트는 http request 요청 시 cookie에 sessionID를 저장하여 보내고,
  • 서버에서는 전달받는 sessionID로 DB에서 session을 조회한다.
  • 클라이언트는 sessionID만 관리하면 된다.

 

 

 

💡 Session의 장점

  • 더이상 유저 정보 등을 cookie에 직접 담아서 전송할 필요가 없다.
    • cookie에 직접 유저정보를 담게되면 보안상 매우 위험하다.
  • sessionID가 탈취되더라도 DB에 저장된 session을 삭제하면 된다.

 

 

 

🚨 서버 인증 기반의 문제점

  • 저장 공간의 용량
    • 세션을 DB에 저장해서 탐색하기 때문에, 유저가 늘어날수록 서버의 램이 과부화된다.
    • 여러 서비스를 유지할 때 A 서비스 sessio, B 서비스 session이 나누어져 있다면, 해당 서비스의 session으로만 사용해야 한다.
  • 확장성의 문제
    • 여러 개의 프로세스를 돌리거나, 서버 컴퓨터를 추가하는 것이 어려워진다. 즉, 확장성이 좋지 않다.
    • 쿠키는 단일 도메인 및 서브 도메인에서만 작동하도록 설계되어 있어, 여러 도메인에서 관리하기가 번거롭다.

 

 

 

이를 해결하기 위해 보통 JWT라는 로그인 방식을 도입한다.

 

 

 


 

✔️ JWT(Json Web Token)

JWT는 Session에 사용자 데이터를 저장하는 전통적인 쿠키 세션 방식과 다르게 
JSON 객체에 사용자 정보의 일부를 담는다.


💡 한마디로, JWT는 session, cookie를 대신하는 의미가 있는 문자열 토큰이다.

 

  1. 클라이언트가 로그인을 위해 인증 정보를 서버에 전송한다. (동일)
  2. 서버는 secret 정보를 이용하여 JWT를 생성하고, 클라이언트에게 전달한다.
  3. 클라이언트는 로컬 혹은 브라우저에 저장해두었던 JWT를 이용하여 요청을 보낸다.
  4. 서버는 JWT가 일치하는지 확인한 후, 응답을 보내준다.

 

 

💡 Token 인증방식의 장점

  • 무상태와 확장성
    • Token은 클라이언트에만 저장되므로 stateless하며, 서버를 확장하기에 적합한 환경을 제공한다.
  • 쿠키를 사용함으로 인해 발생하는 보안 취약점이 사라진다.
  • 토큰을 사용하여 다른 서비스에도 권한을 공유할 수 있다.

 

 

 

❓ Session 인증과 비교한 JWT의 차이점

  • 가장 큰 차이는 서버에 인증 정보를 저장하지 않는다는 점이다.
  • 그렇기 때문에 클라이언트의 요청마다 인증을 위해 DB를 탐색하는 과정이 필요하지 않고, 
  • 저장 공간도 필요하지 않다.

 

 

 

💡 JWT는 언제 사용하면 유용할까?

  1. 권한 부여
    • JWT를 사용하는 가장 일반적인 방식이다.
    • 사용자가 로그인하면 각 후속 요청에 JWT가 포함되어, 사용자가 해당 토큰으로 서비스 및 리소스에 접근할 수 있다.
  2. 정보 교환
    • JWT는 정보를 안전하게 전송하는 좋은 방법이다.
    • 예를 들어, 공개/개인 키 쌍을 사용하여 JWT에 서명할 수 있기 때문에 발신자가 누구인지 확인할 수 있다.
    • 또한 헤더와 페이로드를 사용하여 서명을 계산하므로, 콘텐츠가 변조되지 않았는지 확인할 수 있다.

 

 

 


 

✔️ JWT 구조

JWT는 3개의 점(.)으로 구분된 세 부분으로 구성되며 다음과 같다.
header.payload.signature
  • Header
  • Payload
  • Signature

 

따라서 JWT는 일반적으로 다음과 같다.

xxxxx.yyyyy.zzzzz

 

JWT의 구조

 

 

💡 Header

헤더는 토큰의 타입(종류)(type)과 어떤 알고리즘(암호화 방식)(alg)이 쓰였는지를 나타낸다.

 

헤더 구조

  • 이 json은 Base64Url로 인코딩된 JWT의 첫 번째 부분이다.
  • 보통 HMAC SHA256 혹은 RSA가 사용되며, 토큰을 검증할 때 사용한다.
  • 헤더에는 토큰의 종류와 SIGNATURE 생성을 위해서 어떤 알고리즘을 사용했는지가 명시되어 있다.

 

 

 

💡 Payload

페이로드에는 사용자, 토큰 정보를 나타내는 클레임(claim)으로 구성되어있고, 
key-value 형태이다.
즉, 내가 로그인한 유저임을 증명할 수 있는 기본적인 정보들을 넣는다.

차후에 클라이언트가 다시 토큰을 보내면, 해독해서 DB 내의 유저 정보와 비교한다.

클레임에는 사용자 마음대로 아무런 값을 넣을 수 있다.
(다만, 클레임이 많아질수록 토큰의 길이가 길어진다.)

🚨 payload에 담긴 정보는 인코딩만 되어 있기 때문에,
누구라도 디코딩하여 확인할 수 있으므로 중요한 정보를 담으면 안 된다.

 

페이로드 구조

  • 페이로드는 내가 로그인한 유저임을 증명할 수 있는 기본적인 정보들을 넣는다.
  • 차후에 클라이언트가 다시 토큰을 보내면, 해독해서 DB 내의 유저 정보와 비교한다.

 

 

 

클레임은 3가지의 종류가 있다.

  1. 등록된 클레임 (registered claim)
    • 필수는 아니지만, 운영상 미리 정의된 클레임이다.
    • iss(발급자), exp(만료 시간), sub(제목), aud(대상), nbf(토큰의 활성 날짜) 및 기타로 이루어져 있다.
  2. 공개 클레임 (public claim)
    • JWT를 사용하는 사용자들이 마음대로 정의할 수 있다.
    • 공개용 정보 전달을 위해 사용된다.
    • 충돌 방지를 위해 URI 포맷을 이용해야 한다.
  3. 비공개 클레임 (private claim)
    • 등록되어 있지도, 공개되지도 않은 클레임이다.
    • 당사자간에 정보를 공유하기 위해 만들어진 사용자 지정 클레임이다.
    • 즉, 이곳에 인증 정보 등의 서버와 클라이언트간 필요한 정보를 넣어두는ㄴ 형식이다.

 

 

 

💡 Signature

시그니처는 인코딩된 헤더, 페이로드 그리고 비밀키(secret_key)를 가지고 
헤더에 정의된 알고리즘으로 해싱하여 생성한다.

즉, 토큰을 인코딩하거나 유효성 검증을 할 때 사용하는 고유한 암호화 코드이다.

 

시그니처 구조

  • Signature은 위에서 만든 헤더와 페이로드의 값을 각각 BASE64로 인코딩하고, 
  • 인코딩한 값을 내가 설정한 비밀키를 이용해 헤더에서 정의한 알고리즘으로 해싱을 하고, 
  • 이 값을 다시 BASE64로 인코딩하여 생성한다.
  • 🚨 혹시라도 토큰이 제 3자에 의해 탈취되어서 페이로드의 내용이 변경된다면, 토큰의 값이 크게 변경된다.
  • 헤더와 페이로드는 암호화된 값이 아니라 base64로 인코딩 된 값이기 떄문에 위 이미지처럼 누구나 확인이 가능하다.
    • 그렇기 때문에 토큰을 검증하는 서명(Signature)이 정말 중요하며, 
    • 서명에 사용되는 secret_key를 잘 관리해야 한다.

 

 

 

✔️ 토큰 작동 방식

  • 사용자가 로그인하면 JSON 웹 토큰이 반환된다.
  • 🚨 토큰은 자격 증명이므로 보안 문제를 방지하기 위해 세심한 주의를 기울여야 한다.
  • 일반적으로 토큰을 필요 이상으로 오래 보관해서는 안 된다.
  • 또한 보안이 취약하기 때문에, 민감한 세션 데이터를 브라우저 저장소에 저장해서는 안 된다.
  • 앱에서는 설치된 앱 별로 안전한 저장 공간이 제공되지만, 웹에서는 세션 인증이 나은 선택일 수도 있다.

 

 

사용자가 보호된 경로 또는 리소스에 액세스하려고 할 때마다 

Bearer 스키마를 사용하여 Authorization 헤더에서 JWT를 보내야 한다.

헤더의 내용은 다음과 같다.

Authorization: Bearer <token>
  • 이는 특정 경우에 상태 비저장 권한 부여 매커니즘이 될 수 있다.
  • 서버의 보호된 경로는 Authorization 헤더에 유효한 JWT가 있는지 확인하고, JWT가 있는 경우 사용자는 보호된 리소스에 엑세스 할 수 있다.
  • JWT에 필요한 데이터가 포함되어 있으면 특정 작업에 대해 데이터베이스를 쿼리해야 할 필요성이 줄어들 수 있지만, 항상 그런 것은 아니다.
  • 토큰이 Authorization 헤더로 전송되면 CORS(Cross-Origin Resource Sharing)는 쿠키를 사용하지 않으므로 문제가 되지 않는다. 

 

 

 

✔️ JWT에 관하여...

  • JWT는 DB에 저장하는 방식이 아니기 때문에 DB 리소스가 필요 없다는 장점이 있다.
  • 또한 여러 서비스를 운영할 때 jwt 토큰을 사용하면, session처럼 특정 session DB에 접근하는 게 아니라, jwt 토큰 검증 로직을 통해 여러 서비스 간에 통신에서 권한을 쉽게 제한하고 허가할 수 있다는 장점이 있다.
  • 🚨 단점으로는 
    • jwt 토큰이 탈취되었을 경우 누구나 해당 토큰의 권한을 가지고 서비스를 이용할 수 있다.
    • 누구나 헤더와 페이로드의 내용을 볼 수 있기 때문에, 보안에 위협이 될만한 정보나 개인정보가 있다면 노출될 수 있다. (그래서 그런 정보를 넣으면 안 된다.)
    • 이러한 이유로 보통 jwt 토큰의 만료시간(exp)을 짧게 정하고 
    • 리프래시 토큰(Refresh Token)과 함께 사용한다.
  • 🚨 또 다른 단점으로는 
    • 토큰에 담고있는 정보가 많아질수록 데이터의 크기가 커진다.
      • 그렇게되면, 네트워크 전달시 데이터의 크기로 부하가 생길 수 있다.
  • 💡 리프래시 토큰은 DB에 저장되며 긴 만료시간을 가지고 있다.
  • 사용자는 jwt 토큰이 만료되면, 리프래시 토큰을 이용해 갱신을 하여 사용하게 된다.
    • 앱에서 별도의 재로그인 없이 계속 서비스를 이용할 수 있는 것은 리프래시 토큰 덕분이다.

 

 

 

❓ 만약 리프레시 토큰까지 탈취되면?

→ 비교적 만료시간이 짧은 jwt 토큰은 탈취되어도 금방 만료되기 때문에 보안상 위협이 비교적 적은데 반해, 

리프래시 토큰은 새로운 jwt 토큰을 발급받을 수 있기 때문에 탈취되면 위험하다.

 

💡 이런 경우 토큰의 payload를 확인해서 userid를 블락(block)시키는 방법과, 

해당 유저의 리프래시 토큰을 모두 만료시키는 방법이 있다.

 

 

 

 


 

✔️ JWT의 장단점

 

💡 장점

  • JWT를 이용하면 따로 서버의 메모리에 저장 공간을 확보할 필요가 없다.
  • 서버가 토큰을 한번 클라이언트에게 보내주면, 클라이언트는 토큰을 보관하고 있다가 요청을 보낼 때마다 헤더에 토큰을 실어보내면 된다.
  • 쿠키를 사용할 수 없는 모바일 어플리케이션에는 JWT를 사용한 인증방식이 최적이다.

 

 

🚨 단점

  • JWT는 HTTP를 통해서 전송하기 때문에, 페이로드의 크기가 클수록 데이터 전송에 있어서 비용이 커진다.
  • JWT는 유효기간을 따로 정하지 않는 이상 소멸되지 않기 때문에 장기간 방치시 해킹의 위험이 커진다.

 

 

 

 

 


출처