JWT 토큰이랑 싸우기

로선생·2025년 5월 10일
post-thumbnail

이번에 로그인 기능을 구현하면서 어려웠던 부분이 많아서, 정리해보고자 한다.

  • 인증(Authentication): 사용자가 자기 계정을 사용하려고 할 때 로그인하는 것
  • 인가(Authorization): 한 번 인증을 받은 사용자가 로그인이 유지되는 상태에서 일어나는 일 -> JWT 토큰

로그인 프로세스

보통 서버가 클라이언트 인증을 확인하는 방식은 쿠키, 세션, 토큰 세 가지 방식이 있다.

1) 쿠키 인증

cookie

쿠키는 뭔데?

쿠키는 어떤 웹 사이트에 방문했을 때, 클라이언트의 브라우저에 설치되는 작은 데이터 조각이다.

1) 브라우저가 서버에 요청을 보내면,
2) 서버는 쿠키를 담아 클라이언트에 응답 정보를 보내고
3) 이후 클라이언트는 요청을 보낼 때 마다 매번 저장된 쿠키의 요청을 헤더의 쿠키에 담아 보낸다.

쿠키의 단점

하지만 쿠키 인증 방식은 치명적인 단점이 존재하기 때문에 거의 사용되고 있지 않다.

쿠키는 그 값을 그대로 보내기 때문에 보안에 취약할 수 밖에 없다. 사용자가 쉽게 쿠키에 담긴 인증 정보를 위조할 수 있기 때문이다.

(용량 제한이 있고, 브라우저간 공유가 불가능하다는 단점도 존재한다.)

2) 세션 인증 - 전통적인 방식

쿠키의 보안 이슈로 인하여, 세션은 인증 정보를 서버에서 저장하고 관리한다.

세션은 key에 해당하는 세션 ID와 그에 대응하는 value로 이루어져 있다.

1) 유저가 웹사이트에 로그인하면, 세션이 서버에 저장되고,
2) 서버에서 브라우저 쿠키에 세션아이디를 넣어서 클라이언트에 준다.
3) 쿠키에 정보가 담겨있으니, 브라우저는 모든 요청에 세션 아이디를 담아서 전송하고
4) 서버는 클라이언트가 보낸 세션아이디를 비교해서 인증을 수행한다.

세션 방식의 장점

  1. 한 사용자의 디바이스별 인증을 관리할 수 있다.
  • PC로 접속 시 다른 기기(모바일, 태블릿 등)의 접근을 막을 수 있다.
  • 여러 디바이스에서 접속 중일 때 특정 디바이스의 유저를 로그아웃하게 할 수 있다. (인스타그램)
  1. 하나의 계정 공유를 관리할 수 있다.
  • 넷플릭스처럼 계정 공유의 수도 제한할 수 있다.
  1. 비정상적인 접근 신고가 들어오면, 서버에서 판단하여 해당 세션을 삭제해서 바로 로그아웃시킬 수 있다.

세션 방식의 단점?

  • 세션 아이디 자체는 유의미한 정보를 담고 있지는 않으나, 세션 아이디 자체를 탈취해서 클라이언트인척 위장이 가능하다는 단점이 있다.
  • 서버에서 요청이 많아지면 부하가 심해진다.

3) 토큰 인증 방식

토큰 기반 인증 시스템은 클라이언트가 서버에 접속 시, 서버가 인증되었다는 표식으로 토큰을 부여한다. 이 때, 토큰은 유일해야 한다.
클라이언트는 이 토큰을 쿠키나 스토리지에 저장해두고, 다시 서버에 요청을 보낼 때 헤더에 토큰을 심어서 보낸다.
그러면 서버에서 클라이언트로부터 받은 토큰을 서버에서 가지고 있는 토큰과 일치하는지 여부를 판단하게 된다.

기존 세션 기반의 인증은 서버가 DB에 세션정보를 가지고 있고, 이를 조회하는 과정이 필요하기 때문에 많은 오버헤드가 발생한다.
하지만 토큰은 세션과 달리, 클라이언트에 저장되기 때문에 서버의 부담을 덜 수 있다. 서버는 위조 여부만 판별하면 되기 때문이다.

또한 기본적으로는 쿠키 인증 방식과 FLOW가 비슷하나,
쿠키가 아닌 토큰을 사용하고, 서버에서 토큰 검증 및 인가 처리가 이루어진다는 점이 다르다. 보안적으로 쿠키보다 안전하게 관리될 수 있다.

어떻게 쿠키보다 보안적으로 안전한거냐.. 묻는다면 JWT를 알아야 한다.

JWT 토큰 인증 방식

JWT(Json Web Token)은 Json 객체에 인증에 필요한 정보들을 담은 후 비밀키로 서명한 토큰이다.

쉽게 서술하면, JWT 토큰은 사용자의 인증 정보와 서버의 SecretKey로 서버가 생성한 토큰이다.

서버에서도 해당 토큰에 대한 SecretKey를 가지고 있기 때문에, 토큰의 SecretKey가 다르다면 에러를 뱉는다. 따라서 쿠키 인증 방식보다 안전한 것이다. (이 과정이 바로 토큰 검증이다)

하지만, 토큰을 검증하는 것일 뿐이지 토큰을 관리하는 것은 아니기 때문에 세션보다 보안상 취약하다.
또한 비정상적 유저라고 판단 시 토큰을 만료하거나, 디바이스별 제어 등 세션에서 가능했던 부분들이 불가능하다는 단점이 있다.

JWT의 구조

JWT는 헤더, 내용, 서명의 세 가지의 구조로 구분되어 있다.

1) 헤더: 토큰의 타입, 전자서명시 어떤 알고리즘이 사용되었는지를 저장한다.
2) 내용(Payload): 토큰 만료 시간, 토큰 발급자 등 토큰에서 사용할 정보를 담는다. 다만, 이 내용은 암호화가 되어 있지 않기 때문에 민감한 정보를 담지 않아야 한다.
3) 서명: 가장 중요한 구조이다. 이 부분은 개인키로 암호화되어있기 때문에 실제 서명부가 나오지는 않는다.

JWT - Access Token & Refresh Token

보통 JWT라고 하면, 인증 시 사용되는 Access Token을 의미한다.
그럼 Refresh Token은 뭔가!

토큰의 탈취를 막기 위함이다. 해커가 Access Token을 탈취하면, 접근이 모두 가능하게 된다.
이 탈취를 막기 위해서 Access Token의 유효기간을 아주 짧게 만들기도 하는데, 그 과정은 매우 번거로울 것이다.

이 부분을 해결하기 위한 것이 Refresh Token이다.
Refresh Token은 인증이 아닌, Access Token을 재발급해주는 역할의 Token이다.

1. AccessToken 
처음 로그인 요청 시 서버에서 실제 유저의 정보가 담긴 AccessToken을 발행합니다.
클라이언트는 이 AccessToken을 저장한 후, 요청마다 AccessToken을 보내서
해당 AccessToken을 서버에서 검증 후 유효하면 요청에 맞는 응답을 진행합니다.

2. RefreshToken
처음 로그인 요청 시 서버에서 AccessToken 재발급 용도인 RefreshToken을 발행합니다.
이때, 클라이언트는 RefreshToken을 저장하지 않고 RefreshToken은 보통 서버 DB에 저장됩니다.
RefreshToken이 유효하면, AccessToken의 재발급을 진행합니다.

Refresh Token이 어떻게 번거로움을 줄여주는지 예시를 들어보자.
토큰 탈취 문제 대응을 위해 AccessToken 의 유효 기간이 1시간으로 설정했다.
Refresh Token이 없다면 사용자는 1시간마다 로그인을 해줘야할 것이다.

그러나 RefreshToken은 유효 기간이 7일이라고 해보자.
AccessToken이 1시간이 지나 만료 후 클라이언트가 요청을 보낼 때,
RefreshToken 로직이 추가되면, 서버에서는 인증 실패가 아닌 RefreshToken 검증 단계에 진입한다.

RefreshToken이 유효하다면 그 즉시 클라이언트에게 새로운 AccessToken을 발행해주고,
클라이언트는 그 AccessToken을 받아 재요청을 하게 된다.
따라서, 사용자의 눈에는 별도의 재로그인 과정없이 AccessToken이 만료되지 않은 것처럼 동작하게 된다.

RefreshToken의 유효 기간이 7일이기 때문에,
결국 사용자는 AccessToken의 유효 기간이 7일인 것처럼 사용이 가능하다.
물론 RefreshToken도 해커에게 탈취되면 AccessToken을 해커가 재발급 받을 수 있어 위험하지만,
RefreshToken은 클라이언트에 저장되는 것이 아닌 서버 DB에 저장되기 때문에, 해커 탈취 위험이 적다.

참고)
https://blog.stackademic.com/why-do-websites-offer-cookies-understanding-the-world-of-digital-baking-c4f6063e42cb
https://ksh-coding.tistory.com/113

profile
이제는 이것저것 먹어요

0개의 댓글