[Spring Security] (5) 토큰과 JWT

Park Yeongseo·2023년 9월 23일
0

Spring Security

목록 보기
5/13
post-thumbnail

1. 토큰

사용자는 리소스에 접근하기 위한 인증 프로세스의 결과로 토큰을 얻는다. 토큰은 요청자가 리소스 접근을 허용할 만한 사람인지 확인(인증)하기 위해 요청과 함께 제공되어야 한다.

웹 앱에서는 엔드포인트가 리소스가 되고, 토큰은 일반적으로는 클라이언트가 HTTP 헤더를 통해 보내는 문자열이 된다. 이 문자열은 UUID가 될 수도, JWT가 될 수도 있다.

그렇다면 토큰을 쓰는 이유는 무엇일까? 토큰을 사용함으로써 얻을 수 있는 장점에는 다음과 같은 것들이 있다.

리소스를 요청할 때마다 자격 증명을 공유할 필요가 없다.

  • 스프링 시큐리티 시리즈 (1)에서와 같은 경우, 인증이 필요한 요청마다 헤더에 Authorization : Basic …으로 username과 password를 인코딩해서 보내야 한다.
    • 위 경우 자격 증명이 지속적으로 노출되고, 가로채질 위험도 커진다.
    • 토큰을 이용하면 처음에만(로그인) 자격 증명을 보내고, 이후에는 인증 프로세스의 결과로 받은 토큰의 리소스 접근 권한을 이용할 수 있다.

토큰의 수명을 지정할 수 있다.

  • 한 번 토큰이 가로채지더라도 그 토큰을 계속해서 사용할 수는 없게 할 수 있다.
  • 토큰 노출이 발견되면 해당 토큰을 거부할 수도 있다.
  • 자격 증명을 무효로 하지 않고 토큰 무효로 리소스에 대한 접근을 제어할 수 있다.

클라이언트가 요청 시 보내야 하는 권한 등의 세부 정보를 토큰에 저장할 수도 있다.

  • 서버에서 세션을 관리하지 않고, 세부 정보를 담은 토큰을 통해 클라이언트에서 관리하게 할 수 있다.
    • 서버를 Stateless하게 만들어 부담을 줄일 수 있다.
  • 인증 책임을 시스템의 다른 구성 요소에 위임할 수 있다.
    • 다른 플랫폼에서 발급 받은 토큰을 이용해, 인증 과정을 대신하게 할 수 있다.

2. JWT(JSON Web Token)

토큰은 서버 측에서 사용자를 식별할 수 있다면 무엇이든 될 수 있다. JWT는 IEFTF(Internet Engineering Task Force)에서 만든 토큰의 표준 구현 중 하나로 다음과 같은 구성으로 이루어져 있다.

2.1. JWT의 구성

JWT는 헤더, 페이로드, 시그니처의 세 부분으로 구성되며 각 부분은 마침표로 구분된다. 헤더와 페이로드는 JSON으로 형식이 지정되고 Base64로 인코딩된다. 아래는 jwt.io에서 주어지는 샘플이다.

(1) 헤더

헤더에서는 토큰의 타입과 시그니처의 해시 함수 종류를 지정한다. (각각 예시의 typalg)

(2) 페이로드

페이로드에는 권한 부여 등에 필요한 세부 정보들을 저장한다. 이 페이로드에는 여러 종류의 클레임들이 포함되어 있다.

  • Registered Claims

    • 토큰 자체에 대한 정보들을 담기 위해 표준으로 정해진 클레임.
    • 사용이 필수인 것은 아니지만, 서드파티 앱과의 interoperability를 위해 사용하는 것이 권장됨
    • iss (issuer): JWT 발급자
    • sub (subject): JWT 제목(흔히 이메일 사용)
    • aud (audience): JWT 대상자
    • exp (expiration time): 만료 시간
    • nbf (not before time): JWT가 활성화되는 시간(이 시간이 지나기 전에는 토큰 처리가 되지 않음)
    • iat (issued at time): 토큰 발급 시간
    • jti (JWT ID): JWT의 고유 식별자. 토큰을 단 한 번만 사용하게 하기 위해 사용
  • Custom Claims

    • 위와 달리 임의로 클레임을 지정해서 사용할 수도 있다.
    • Public
      • 공개용 정보들을 담은 클레임
    • Private
      • 비공개 정보를 담은 클레임

(3) 시그니처

토큰 인코딩 및 유효성 검증에 사용하는 디지털 서명이다. 헤더와 페이로드를 Base64 인코딩하고, 지정한 비밀 키로 헤더에서 정의한 알고리즘을 이용해 해싱한 후 다시 Base64로 인코딩해서 만든다.

서명을 이용하면 JWT가 중간에 가로채져 내용이 변경되거나 하지는 않았는지 확인할 수 있다. (위 캡처의 VERIFY SIGNATURE를 참고)

2.2. JWT의 특징

(1) 장점

  • Stateless하게 유지할 수 있다.
    • 서버에서 사용자 요청이 유효한지 확인하기 위해 세션을 만들어 유지할 필요가 없다.
    • 서버의 부담이 줄어듦
  • 작다
    • Base64로 인코딩 되어 있고, URL, HTTP 헤더, HTTP POST 파라미터 등으로 쉽게 전달된다
  • 암호 서명
    • JWT를 발급하는 인증 서버에 의해 서명되어 있다.
    • 중간에 가로채져 변형되는 경우를 체크할 수 있다.
  • Self-contained
    • JWT는 암호 서명되어 있기에 이를 받는 서비스는 토큰의 내용이 유효한지를 확인할 수 있다.
    • Payload에 인증에 필요한 정보들을 담고 있기 때문에 이를 가지고 다시 인증 서버에 유저 정보를 요청할 필요가 없다.
    • 시간과 자원이 많이 드는 DB 조회 과정을 생략할 수 있다
  • 확장성
    • 인증 서비스에서는 토큰을 발급할 때 여러 필요한 추가 정보들을 넣을 수 있다.
    • 토큰을 사용하는 다른 서비스들 또한 이 토큰을 복호화하고 해당 정보들을 이용할 수 있다.
  • 모바일 환경에서도 잘 작동함

(2) 단점

단점은 대부분 장점과의 trade-off들이다.

  • 작아야 한다
    • 페이로드에 과도하게 정보를 담아 토큰이 너무 커지면 요청의 속도가 느려지고, 토큰 서명 시 암호화 알고리즘 서명 시간 또한 길어진다.
  • Payload의 취약한 암호화
    • Base64는 디코딩할 수 있다.
    • 토큰이 중간에 탈취되는 경우, 내부에 담긴 모든 정보를 확인할 수 있다.
      • 따라서 payload에는 사용자에게 sensitive한 정보는 담지 않는 것이 좋다
  • 토큰이 탈취 되었을 때의 대처가 어렵다.
    • Stateless하므로, 토큰의 임의 삭제가 불가하다.
    • 토큰 만료 시간을 넣어서 관리해야 한다.

위와 같이 JWT는 통신 중에 탈취되는 경우 많은 취약점을 가지므로, JWT를 사용할 때에는 반드시 HTTPS를 이용한 트래픽 암호화가 필요하다.

0개의 댓글