JWT, 왜 사용하는가?

뾰족머리삼돌이·2025년 6월 3일
0

Spring Security

목록 보기
16/16

최근 어떤 기업 기술면접을 다녀왔는데, JWT와 사용자 인증 관련 질문을 받았다. 나름 RFC문서도 조사하고, 학습 및 정리했던 부분이라 수월하게 답변할 수 있을거라 생각했지만 면접관님과 대화하는 과정에서 스스로에게 ?가 띄워져서 추가적인 답변을 못했다.

스스로에게 과연 나는 합리적인 판단하에 토큰인증을 선택했는가?라는 질문을 던지게된 면접이니만큼 해당 주제로 글을 작성하려한다.

사용자 인증

사용자 인증은 웹 사이트에 회원가입/로그인의 개념이 존재하는 한, 반드시 결정해야하는 숙제와도 같다. 서비스에는 사용자가 함부로 접근해서는 안되는 데이터도 있기때문에 각 요청은 사전에 권한을 부여받아야 하며, 이 권한이 자원 접근에 적절한 권한인지 평가받아야 한다.

그러나, HTTP 통신은 무상태성(stateless) 라는 특징을 가지기때문에 각 요청들은 독립적으로 처리된다. 즉, 동일한 클라이언트에서 서버에 연속적인 요청을 보내더라도 서버는 해당 요청의 수신자를 판별해낼 수 없다.

웹 통신에서 HTTP통신은 TCP 위에서 동작하기에 논리적인 연결수립이 수행되고, 상태를 가진다.

상태를 가진다라는 개념은 서로 통신중인 클라이언트와 서버의 관계에서
요청에 대한 응답 여부, 상대방은 어떤 처리상태에 놓여있는지 등을 확인할 수 있다는 의미에 가깝다.

이러한 HTTP의 특성과 웹 서비스의 요구사항이 맞물려서 반드시 수행해야하는 과제가 사용자 인증방식 인 것이다.

세션/쿠키와 토큰인증

사용자 인증은 보통 크게 2가지 방법( 세션/쿠키, 토큰 ) 으로 구분된다.
간략하게 설명하자면,

  • 세션/쿠키 방식은 유저의 인증정보를 서버에 저장하고, 이를 식별할 수 있는 식별값( 세션ID )을 유저에게 내려주는 방식이다.
  • 토큰 방식은 유저에게 인증했다는 증거( 토큰 )을 내려주고, 이 토큰을 검증할 수 있는 방법은 서버가 보유하는 방식이다.

가장 큰 차이점은 서버가 유저의 인증정보 관리를 위한 공간을 마련하느냐? 라고 볼 수 있다. 토큰방식은 공간을 따로 마련하지는 않지만, 공개키 or 비밀키 방식으로 자신이 서명한 증거를 증명할 수 있다. 반면, 세션/쿠키 방식은 세션이라는 보관장소에 인증정보를 저장하고 쿠키를 통해 식별값을 내려준다.

작성자 본인은 처음 프로젝트를 진행했을 때, SPA, REST API, RESTful 이라는 개념들이 유행(?)처럼 퍼져있는 상황이었고 REST API의 개념적 특징( 요청 간 무상태성, HTTP Method를 이용한 API 식별 등 )을 만족하기에 적합한 인증방식으로 토큰인증을 선택했었다.

토큰인증과 모순의 시작

우선 토큰인증을 선택한 이상, 하나의 대전제를 하나 깔고 들어가야했다.

서버는 사용자의 인증상태를 관리하지 않으며, 검증이 가능한 토큰을 유저에게 내려준다.

개발자체는 나름대로 순탄하게 흘러갔다. Spring Boot 환경에서 개발을 진행했고, 사용자 인증을 필터단에서 독립적으로 처리할 수 있다는 매력에 이끌려 Spring Security 프레임워크를 도입했다.

Spring Security를 학습하는 과정에서 SecurityFilterChain의 동작흐름과 인증-인가 순으로 동작하는 피렅순서에 대해 학습했다. 또한, Filter-Manager-Provider의 단계를 거쳐 Authentication 객체를 생성하는 방식으로 로그인 필터를 작성했다.

이후에는 서버로 들어오는 각 요청들의 토큰정보를 얻어내고, 이를 바탕으로 인증된 요청인지를 판단하기 위해 JWT 인증 필터 를 생성했다. 문제는 토큰 탈취에 대해 고민하면서 시작됐다.

앞서 소개한 것처럼 토큰인증은 사용자에게 증거( 토큰 )을 내려주고, 요청에서 이 증거를 검증하는 방식으로 인증을 수행한다. 다르게 말하면 토큰을 탈취할 수 있다면 서버는 해당 사용자가 보낸 요청이라고 인식한다는 말이다. 이는 분명 심각한 문제를 초래할 것이기 때문에 서버입장에서는 반드시 해결해야하는 문제와 같았다. 곧바로 해결책을 찾기위해 인터넷을 뒤지기 시작했고, 이윽고 토큰 분리라는 해결책을 찾을 수 있었다.

토큰 분리는 서로 다른 역할을 하는 2개의 토큰( Access/Refresh )으로 분리하는 것으로 시작한다.

  • Access는 하나의 토큰을 사용할 때의 그 토큰과 역할이 동일하다.
    • 사용자를 식별할 수 있어야 하므로 인증정보( ID등 )를 가지고 있어야한다.
    • 탈취당하더라도 빠르게 효력을 잃도록, 토큰이 가진 유효기간을 짧게 설정
  • Refresh는 Access가 만료되었을 때, 재발급을 위한 역할을 한다.
    • Access가 만료 될때마다 로그인을 반복하게 되는 UX저하를 막기위해 토큰을 재발급하기 위한 역할을 맡으며,
      Access보다 유효기간을 길게 설정

위 설명에서 보다시피, Refresh는 토큰의 재발급이라는 책임을 가지고 있다. Refresh가 탈취당하게 된다면 토큰의 유효기간동안 계속해서 토큰을 재발급받으며 정상적인 사용자처럼 동작을 수행할 수 있을 것이다. 또한, 로그아웃과 관련해서도 토큰이 계속해서 유효하다면 진정한 의미의 로그아웃이 아니게 되는 점도 있다.

따라서, Refresh를 1회용으로 사용하기 위해 유효한 Refresh를 관리해야할 필요가 생긴다. 유효한 Refresh로 재발급을 진행하면 기존 토큰을 무효화시키고 새로운 토큰 쌍을 생성한 뒤, 유효한 Refresh 토큰을 갱신해주는 방식이다. 이를 위해서는 서버측에서 어떤 Refresh 토큰이 유효한지 판단하기 위해 토큰 정보들을 관리하고 있어야한다.

정상적인 사용자라면 자신의 인증정보가 잘못되었다는 알림을 받았을 때, 아래의 사고방식으로 대처가 가능해진다.

새롭게 로그인을 진행 => 유효한 Refresh 토큰 변경 => 탈취당한 토큰으로 재발급을 받더라도 빠르게 효력을 잃음

문제는 여기서 모순이 발생한다는 점이다. 앞서, 토큰 인증의 대전제가 기억나는가?

서버는 사용자의 인증상태를 관리하지 않으며, 검증이 가능한 토큰을 유저에게 내려준다.

유효한 Refresh 토큰을 관리하기 위해 서버는 결국 저장소인 DB나 Redis 등을 사용하게 된다. 이 말은 서버가 상태를 관리한다는 말이되며, 토큰인증의 중요한 특성 중 하나와 모순되는 결과를 낳게된다.

결국 이런저런 목적에 의해 토큰을 분리하고, 목적을 나누었지만 서버에서 정보를 관리하고( 세션/유효한 Refresh ), 이를 식별할 수 있는 식별자( 쿠키/Refresh )를 클라이언트에게 내려준다. 라는 점에서 쿠키/세션과 다를바가 없게된다. 어차피 저장소를 사용할 것이라면 굳이 헤더크기가 증가하는 토큰방식을 선택할 이유도 없으며, 쿠키/세션을 선택하는게 더 효과적일 수도 있다.

이 부분과 관련된 모순을 과거의 나는 회피했었고, 이와 관련하여 면접에서 질문을 받은 뒤 그에대해 후회했다.

당신은 JWT를 왜 사용하시나요?

앞서 소개했던 모순이 있음에도 JWT는 분명 유효한 인증 방식이다. 과연, JWT의 어떤 특징이 매력적이길래 계속해서 사용될 수 있는걸까? 우선 JWT의 구조를 간단히 살펴보고, 명확한 해답이라고 볼 수는 없지만 나름의 이유를 말해보고자 한다.

먼저, JWT는 {Header}.{Payload}.{Signature}로 구성된다.

Header에는 쿠키에 적용된 알고리즘이나 종류 등에 대한 정보가 표기되어있다.
Payload에는 Claims 라는 명칭으로 유저 식별자, 발행일자, 만료일자 등이 작성된다.
Signature는 Base64로 인코딩된 Header와 Payload를 포함한 서버의 서명정보가 들어있다.

대칭키 기준으로, 서버는 [ Signature와 토큰정보를 서버의 대칭키로 암호화한 내용이 동일한가? ] 에 따라 유효성을 검증한다.

Header와 Payload는 Base64로만 인코딩되기 때문에 손쉽게 복호화가 가능하며, 그 속에 담긴 데이터를 손쉽게 확인할 수 있다.


JWT가 유효한 인증 방식인 이유 중에서 모바일 환경이 중요한 부분을 차지한다고 생각한다. 세션/쿠키 인증에 사용되는 쿠키라는 개념은 보통 웹 브라우저에서 유효한 개념이다. 동일한 서버로 요청을 보낼 때 쿠키가 항상 함께 전달되므로, 세션ID를 쿠키에 저장하여 서버의 세션을 조회하는 방식인 것이다.

참고 블로그를 살펴보면, 네이티브 앱에서는 브라우저가 없기때문에 이러한 쿠키개념이 사용되기 번거롭고, 그렇기에 토큰인증이 선택된다는 내용이 있다. 즉, 모바일 환경을 고려했을 때 쿠키/세션 방식의 메리트가 줄어들고 그렇기에 모바일 환경이 고려되는 상황에서는 토큰인증을 사용한다는 것이 나름의 결론이다.

AccessToken이 유효한 상황에서는 토큰인증의 특징인 무상태성을 만족하기도 한다. 즉, 저장소 접근없이 인증처리가 가능하다.

0개의 댓글