Post

JWT 개념과 탈취 대안까지의 핵심 정리

이 포스팅은 Cookie, Session, Token을 안다는 가정 하에 진행됩니다.

대부분의 사용자 인증/인가 작업에 JWT를 알아보고 탈취 위험과 이를 방지하기 위한 기법까지 알아보겠습니다.

JWT(Json Web Tokens)

  • 서버에서 상태를 저장하는 Session 방식의 몇가지 한계를 극복하기 위해 stateless 방식으로 개발되었다.

    세션 방식은 서버의 자원을 많이 사용하고, 서버 확장성에 제약이 있다는 한계가 있다.

  • 토큰 형식이다.

    모바일 환경에서 쿠키처럼 사용하기 위해 Token 이라는 개념이 도입됐다.

  • 랜덤한 문자열을 갖는 토큰과 달리 형식이 정해져 JWT 안에 여러 정보들을 넣을 수 있다.


JWT의 구조

  • Header, Payload, Signature 3가지 구조로 이루어져 있다.
  • Header, Payload 는 안전하게 전송할 수 있도록 Base64로 인코딩된다.

    이 인코딩은 암호화가 아니고, 쉽게 디코딩 가능하다.

  • Signature는 인코딩한 Header, Payload 와 서버에서 보관하는 비밀 키를 사용하여 암호화한다.


[JWT access/refresh 예시]

JWT 확인 사이트

interceptor-flow interceptor-flow


  • 주로 typ, alg 두가지 정보를 담고 있다.
  • typ (Type)
    • 토큰의 타입을 명시한다.
    • 일반적으로 JWT 라고 표시된다.
1
2
3
4
{
  "alg": "HS256",
  "typ": "JWT"
}


[Payload]

  • 서비스에 필요한 정보들을 담는다.
  • 쉽게 디코딩되므로 절대 민감한 정보를 담아서는 안된다.
1
2
3
4
5
6
7
{
  "category": "access",
  "username": "admin",
  "role": "ROLE_ADMIN",
  "iat": 1714888832,
  "exp": 1714889432
}


[Signature]

  • 헤더의 alg 정보에 있는 알고리즘으로 암호화된다.
  • 알고리즘의 인자로는 인코딩된 Header, Payload비밀키가 사용된다.

    이때 사용되는 비밀키는 서버만 알고, 서버만 가지고 있다.

  • 서버의 유효 토큰 확인
    • 서버는 Signature를 비밀키로 해독하여 암호화에 사용된 Header, Payload 정보를 얻는다.
    • 해독으로 얻은 정보들과 받은 정보들을 비교하여 토큰이 조작되었는지 여부를 확인할 수 있다.

      Digital Signature

    • 서버는 토큰이 조작되지 않음을 확인함으로써 올바른 사용자임을 확인한다.
    • 이후 payload의 정보들을 통해 토큰이 올바른지, 만료되지 않았는지 추가로 확인한다.


탈취 위험

  • 네트워크 통신에서 오가는 jwt 토큰을 완벽히 숨길 수는 없기 때문에 결국 탈취 위험이 존재한다.
  • 탈취자가 토큰을 가지고 특정 사용자인척 서비스를 사용하면 진짜 사용자는 피해를 입게 될 수 있다.
  • 이를 완벽하게 차단할 방법은 없지만 피해를 최소화하기 위해 단일 토큰이 아닌 access/refresh 다중 토큰을 주로 사용한다.


access/refresh 다중 토큰

  • 네트워크 상에 노출될수록 토큰이 탈취당할 위험이 높다 를 전제로 토큰을 나누었다.
  • 사용자가 로그인할 때 서버는 access 토큰과 refresh 토큰을 발급해준다.


[access 토큰]

  • 인증/인가에 사용되는 토큰이다.
  • 사용자는 매 요청마다 access 토큰을 헤더에 담아 요청한다.
  • 자주 전달되어 탈취당할 위험이 높으므로, 탈취당하더라도 그 피해를 최소화하기 위해 유효기간을 짧게 설정한다.(약 10분)
  • access 토큰이 만료되면 refresh 토큰을 통해 새로 발급받는다.


[refresh 토큰]

  • 오직 access 토큰을 발급하는 데에만 사용한다.
  • access 토큰보다 덜 전달되어 탈취당할 위험이 상대적으로 적으므로 유효기간을 길게 설정한다.(24시간 이상)

  • 그러나, 상대적으로 탈취 위험이 적은 것일뿐, 0%는 아니다.
  • 탈취 당했을 때의 피해를 줄이고자, Refresh Token Rotation(RTR) 기법이 사용된다.


Refresh Token Rotation

  • refresh 토큰을 일회용으로 설정하여 사용자가 refresh 요청을 할 때마다 access 토큰 뿐만 아니라, refresh 토큰도 새로 발급해준다.
  • 이전 refresh 토큰은 만료처리한다.
  • 만약 만료된 refresh 토큰으로 요청이 온다면 해당 사용자의 모든 refresh token을 무효화시킨다.
  • 사용자는 재로그인 해야만 서비스를 다시 이용할 수 있다.
  • 탈취자는 유효한 refresh 토큰을 다시 탈취하지 않는 이상, 서비스를 다시는 이용할 수 없다.


[탈취 시나리오]

interceptor-flow

Malicous Client : 공격자, Legitimate Client : 사용자

RT : refresh token, AT : access token

  1. 공격자가 RT(1)을 탈취한다.
  2. 공격자가 RT(1)으로 AT를 요청한다.
  3. AT(2)와 RT(2)가 공격자에게 반환된다.
  4. RT(1)가 무효화되고, 사용자가 RT(1)으로 AT를 요청한다.
  5. 재사용이 감지되어 RT Family가 무효화 된다. 서버는 사용자에게 Access Denied 응답한다.
  6. 공격자가 RT(2)로 AT를 요청한다.
  7. 모든 RT가 무효화되었으므로 서버는 Access Denied를 응답한다.


출처

인증과 인가를 안전하게 처리하기 (Refresh Token Rotation)


누군가가 물어본다면

JWT는 기존 세션 방식의 성능, 확장성 문제를 해결하기 위해 stateless 구조로 설계되었습니다.

하지만 탈취 위험은 여전히 존재했고 이 피해를 최소화하기 위한 Refresh Token Rotation 등의 기법이 사용되고 있습니다.

This post is licensed under CC BY 4.0 by the author.