본문 바로가기
Spring

JWT(Json Web Token)를 이용한 로그인 인증에 대하여

by 민죠미 2023. 4. 5.
졸업프로젝트 시 로그인 구현에 있어서 어려움을 겪었다. 초기엔 쿠키-세션 방식으로 구현하였으나 프론트와의 상의 후 JWT로 바꾸기로 했었고 이 과정에서 JWT와 Spring Security를 조금 공부했었다. 당시 정리한 내용에 살을 붙혀 포스팅 해본다.

JWT(Json Web Token) 란?

JWT(Json Web Token)은 일반적으로 클라이언트와 서버 통신 시 권한 인가(Authorization)을 위해 사용하는 토큰이다.
토큰 이란 간단히 말하면 로그인 이후 서버가 만들어서 사용자에게 넘겨주는 문자열이다. 이 문자열은 사용자 정보가 암호화 되어있고 이 토큰을 이용하여 인증된 사용자인지 서버가 판단한다.

JWT 구조

JWT는 세 파트로 나누어지며, 각 파트는 점로 구분하여 xxxxx.yyyyy.zzzzz 이런식으로 표현된다. 순서대로 헤더 (Header), 페이로드 (Payload), 서명 (Sinature)로 구성한다.

1) header

  • 토큰의 종류와 해싱 알고리즘의 정보 이렇게 2가지 정보가 담겨 있다.
  • typ: 토큰의 타입을 지정한다. 바로 JWT를 말하는 것이다.
  • alg: 해싱 알고리즘을 지정한다. 보통 HMAC-SHA256 혹은 RSA 가 사용되며, 이 알고리즘은 토큰을 검증 할 때 사용되는 signature 부분에서 사용된다.

2) payload

  • 토큰의 내용물이 인코딩된 부분 (토큰에 담을 정보가 들어 있는 부분)
  • 여기에 담는 정보의 한 ‘조각’ 을 클레임(Claim) 이라고 부르고, 이는 Json(Key/Value) 형태의 한 쌍으로 이뤄져있다. 토큰에는 여러개의 클레임들을 넣을 수 있다.

3) Signature

  • 토큰을 인코딩하거나 유효성 검증을 할 때 사용하는 고유한 암호화 코드
  • 헤더(Header)와 내용(Payload)의 값을 각각 BASE64로 인코딩하고, 인코딩한 값을 비밀키를 이용해 헤더에서 정의한 알고리즘으로 해싱을 하고, 이 값을 다시 BASE64로 인코딩하여 생성한다.
  • secret key로 사용되는 일련의 문자열이라고 이해하면 쉽다.

쿠키-세션 vs 토큰

웹 어플리케이션에서 로그인을 구현하는 방법은 크게 쿠키, 세션을 이용하는 방법과 토큰을 이용하는 방법 2가지가 있다.
쿠키, 세션 방식은 서버에서 세션아이디를 기록하고 브라우저에서 쿠키를 저장하는 방법이고 토큰 방식은 토큰을 발행하여 토큰의 유효성을 검증하는 방법이다.

쿠키-세션 (cookie, session)

  1. 사용자(클라이언트)가 서버에 자원을 요청 시 서버에서 사용자를 특정할 수 있는 데이터를 기반으로 세션 아이디를 생성
  2. 해당 아이디를 키값으로 하여 필요한 값들을 서버의 메모리에 저장
  3. HTTP 프로토콜의 header의 setCookie를 통해서 브라우저의 쿠키에 세션 아이디를 기록
  4. 브라우저는 request 요청을 보낼 시 header에 cookie를 자동으로 포함하여 전송
    (cors일 경우 credentials 등 세팅이 필요)
  5. 서버에서는 cookie의 세션 아이디를 토대로 사용자의 로그인 등을 판별

장점

  • 토큰 방식은 토큰 안에 사용자에 대한 정보를 함께 넣기 때문에 세션 아이디만 전달하는 방식은 전송되는 데이터의 양이 토큰 방식보다 적을 가능성이 높다.
  • 사용자의 데이터를 서버에서 관리하기 때문에 사용자의 데이터가 토큰에 담겨있는 것보다 안전
  • 세션은 서버에 로그인 된 유저의 모든 정보가 저장되므로 해당 정보를 이용하면 새로운 기능을 추가할 수 있다.
    • 세션을 삭제하여 강제 로그아웃, 넷플릭스처럼 현재 로그인을 몇 명이 했고 시청하는지 확인
    • 서버가 누가 로그인 했는지 저장했고, 세션DB가 있기 때문에 가능. DB로는 주로 Redis를 사용 (빠르고 저렴)

단점

  • 서버의 성능을 늘리는 방식을 scale-out으로 할 경우 각 서버의 세션을 동기화 해야하는 비용이 추가적으로 발생 할 수 있다.
  • 사용자의 데이터를 서버의 메모리에 저장하기 때문에 메모리 용량에 대한 리스크가 있을 수 있다.

 

토큰 방식 (token, JWT)

  • 사용자가 로그인을 요청 시 서버에서는 어떠한 데이터를 기반으로 인증이 가능한 토큰을 만들어서 클라이언트에 전달
  • 클라이언트는 요청 시 토큰을 서버로 보내고 서버는 토큰에 대한 유효성을 검사하여 사용자가 인증되었는지 판별
  • 토큰방식은 토큰의 유효성을 확인 할 수만 있다면 유저의 인증을 별도로 분리할 수 있다
    • 구글 로그인을 통해서 구글에서 발급한 토큰을 구글의 토큰 인증 서버를 통해 유효성을 검증하여 사용자를 인증
  • JWT는 자체적으로 Claims를 인코딩하고 서명(signing)을 통해 확인 (verify) 할 수 있다
    • 즉, 상태를 저장하지 않음 (stateless) 으로 DB 혹은 저장소 (Persistence Layer)가 필요 없다
    • JWT만으로도 많은 것들을 확인 가능하여, 해당 Application 에서 인증 관련 프로세스의 간소화가 가능하다 (DB 로드가 줄어들기 때문에)
    • JWT를 발행(publish)하는 서비스만 refresh token 발행으로 인한 DB/Persistence Layer 에 대한 대비를 할 필요가 있다 (refresh token 저장은 해야함)

장점

  • 사용자의 인증을 토큰을 통해서 관리하기 때문에 서버의 scale-out에서도 인증이 자유롭다
  • stateless 서버를 만들 수 있음
  • 사용자에 대한 인증 방식의 확장이 가능(facebook, google 등 소셜 로그인)
  • 헤더와 페이로드를 가지고 서명 필드를 생성하므로 데이터 변조 후 재전송을 막을 수 있다
  • 모바일 어플리케이션에서도 잘 동작한다

단점

  • 인증된 토큰을 발급을 하면 토큰이 expire되기 전까지 토큰의 유효성을 막을 방법이 없다.
    • 사용자를 block을 시켜야 할 때 세션방식은 세션 아이디를 파기하여 즉시 block할 수 있지만, 토큰방식은 발급한 토큰이 expire 되기 전까지 사용자에 대한 인증이 유효할 수 있다.
  • 여전히 누구나 디코딩이 가능하므로 데이터 유출이 발생할 수 있다
  • JWT의 경우, 토큰의 길이가 길기 때문에 요청이 많아질수록 서버 자원의 낭비가 많아진다

JWT 기반 로그인 동작 과정

  1. 사용자는 id(or email) 과 password를 입력하고 서버로 요청
  2. 서버는 db에서 회원을 조회하고 등록된 사용자인지 확인
  3. 등록된 사용자라면 서버는 토큰을 생성하고 프론트로 토큰과 함께 응답
  4. 응답이 성공적으로 왔다면 로그인(인증)이 성공적으로 이루어 진 것이고 이후 요청에 token을 함께 보낸다
  5. 서버는 로그인 이후의 요청에 함께 실려온 토큰을 검증하는 과정을 검증이 성공적으로 끝났다면 권한이 있는 사용자라고 생각하고 요청된 데이터를 응답 (인가)

Access Token 과 Refresh Token

token은 발급 시 유효기간 동안 되돌릴 수 없고 access token을 탈취당할 시 인증 정보가 모두 담겨있으므로 위험하다.
이를 해결하기 위해 서버에서는 access token과 refresh token 두개의 토큰을 발행한다.

  • Access Token : 말 그대로 인증된 유저인지 서버에서 검증하는 토큰. client 쪽에서 요청 헤더에 담아 보내는 토큰이다.
  • Refresh Token : access token이 만료되거나 잘못된 토큰일 경우 refresh token을 사용해 유저를 검증한다. 만약 refresh token을 검증하여 인증된 유저일 경우 access token을 재발행 해준다.

access token의 유효기간을 짧게 설정하고 refresh token의 유효기간을 길게 설정하여 access token 만료시 refresh token을 검증하여 access token을 재발행 해주는 것으로 보안적으로 안전하게 유저 인증을 관리 할 수 있다.

그런데, 만약 Refresh Token이 유출되어서 다른 사용자가 이를 통해 새로운 Access Token을 발급받았다면?

Refresh Token의 탈취?

refresh token 또한 탈취당할 수 있기 때문에 서버측의 검증 로직이 필요하다.

  • 데이터베이스에 각 사용자에 1대1로 맵핑되는 Access Token, Refresh Token 쌍을 저장한다.
  • 정상적인 사용자는 기존의 Access Token으로 접근하며 서버측에서는 데이터베이스에 저장된 Access Token과 비교하여 검증한다.
  • 공격자는 탈취한 Refresh Token으로 새로 Access Token을 생성한다. 그리고 서버측에 전송하면 서버는 데이터베이스에 저장된 Access Token과 공격자에게 받은 Access Token이 다른 것을 확인한다.
  • 만약 데이터베이스에 저장된 토큰이 아직 만료되지 않은 경우, 즉 굳이 Access Token을 새로 생성할 이유가 없는 경우 서버는 Refresh Token이 탈취당했다고 가정하고 두 토큰을 모두 만료시킨다.
  • 이 경우 정상적인 사용자는 자신의 토큰도 만료됐으니 다시 로그인해야 한다. 하지만 공격자의 토큰 역시 만료됐기 때문에 공격자는 정상적인 사용자의 리소스에 접근할 수 없다.

 


Reference.

댓글