JWT(JSON Web Token) 이란?

주의

이 문건은 과거 Hexo 블로그 (2018-08-12) 에서 이동된 문서입니다.

시간이 지남에 따라 최신 기술과 다를 수 있으니 주의 바랍니다.



인증과 토큰 그리고 JWT?

최근들어 보안 및 인증을 위해서 JWT를 사용하게 되었다.
그래서 사용만 하다가 이번에 JWT에 대한 개념과 구조, 사용법과 문제점 등을 알아보고자 한다.


일반 토큰 기반의 인증과 클레임(Claim) 토큰 기반 인증

일반 토큰 기반은 과거에 많이 사용하던 방식이다.
주로 의미가 없는 문자열(Random string) 기반으로 구성되어 있으며 아래와 같이 표현이 된다.

a9ace025c90c0da2161075da6ddd3492a2fca776

그리고 사용할 때는 아래와 같은 json으로 보내는 방식으로 사용하게 됩니다.

{
  "code": 0,
  "msg": null,
  "response": {
    "token": "a9ace025c90c0da2161075da6ddd3492a2fca776",
    "now": 1512446940,
    "expired_at": 1512448740
  }
}

그런데 이런 일반 토큰은 단순한 문자열이기 때문에 정보를 담거나 할 수 없다.
크게 보자면 아래와 같은 문제점을 가지고 있다.

  • 발급된 토큰에 대해서 만료를 시킬 수단이 없다.
  • 발급된 토큰을 검사하거나 처리할 때마다 DB에 접근하여 검사할 경우 부담이 생긴다.
  • 사용자 로그아웃 등으로 인한 토큰을 관리할 수 있는 방법이 없다.

이와 같은 문제를 어느정도 해결할 수 있는 것이 클레임(Claim) 기반 토큰 방식이다.

클레임(Claim)이란 사용자 정보나 데이터 속성 등을 의미한다.
그래서 클레임 토큰이라 하면 토큰 안에 저런 정보를 담고 있는 토큰이라 생각하면 된다.
예를 들면 아래와 같이 정보를 담고 있는 것을 클레임 기반이라 할 수 있다.

{
  "id":"mhlab",
  "role":["admin","hr"],
  "company":"hexlant"
}

이런 클레임을 기반한 토큰 중 가장 대표적인 것이 바로 JWT다.


What is JWT?

JWT(Json Web Token)은 위에서 이야기 한 클레임 기반 토큰이며,
이름에서 알 수 있는 것처럼 JSON을 이용한 토큰이고 웹 표준(RFC 7519)를 구현한 것이다.
자세한 것은 JWT 공식페이지에서 확인 가능하다.

JWT는 헤더(header), 페이로드(payload), 서명(signature) 세 가지로 나눠져 있으며, 아래와 같은 형태로 구성되어 있다.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

각 구분은 . 구분자로 나눠 표현되며, 각 값은 BASE64로 인코딩 되어 있다.


헤더에는 typalg 두 가지 정보로 구성되어 있다.

  • typ * 토큰의 타입을 지정 (jwt)
  • alg _ 해싱 알고리즘을 지정
    _ 주로 HMAC SHA256 또는 RSA를 사용하며 이 알고리즘은 서명(signature)에서 사용한다.

헤더 형태는 아래와 같다.

img 01


Payload

페이로드라 불리는 부분에는 토큰에서 사용할 정보가 담겨있고 이를 위에서 설명한 클레임이라 부르는 것들이 저장되어 있다.
Key/Value 방식으로 이뤄져있으며 (JSON과 유사) 다수의 정보를 넣을 수 있다.
이 클레임은 총 세 가지로 나뉜다.


등록된 클레임 (Registered Claim)

등록된 클레임은 토큰 정보를 표현하기 위해 이미 정해진 데이터 종류이며, 모두 선택적으로 작성 가능하다.
종류는 아래와 같다.

  • iss: 토큰 발급자 (issuer)
  • sub: 토큰 제목 (subject)
  • aud: 토큰 대상자 (audience)
  • exp: 토큰의 만료시간 (expiraton) * NumericDate 형식으로 되어있어야 한다 (Ex: 1480849147370)
  • nbf: 토큰 활성 날짜 * 이 날짜가 지나기 전 토큰은 활성화 되지 않는다.
  • iat: 토큰이 발급된 시간 (issued at) * 이 값을 사용하여 토큰 발급 이후 얼마나 시간이 지났는지를 알 수 있다.
  • jti: JWT의 고유 식별자 * 중복 방지를 위해 사용하며, 일회용 토큰(Access Token 등)에 사용한다.

공개 클레임 (Public Claim)

공개 클레임은 서로 충돌이 일어나지 않는 이름을 가지고 있어야한다.
그래서 URL 형태로 작성하며 예시는 아래와 같다.

{ "https://hexlant.com": true }

비공개 클레임 (Private Claim)

비공개 클레임은 실제 사용을 하는 개발자가 지정하는 것으로 서버와 클라이언트가 서로 정의하여 사용하는 클레임을 의미한다.
아래와 같이 사용하기도 한다.

{ "token_type": "access" }

결론적으로 페이로드는 아래와 같이 구성된다.

img 02


Signature

서명은 위에서 만든 HeaderPayload 의 각 값을 BASE64로 인코딩 하고,
그 값을 비밀키를 이용해 헤더에서 정의한 알고리즘으로 (HS256이나 SHA256 등) 해싱을 하고,
이 값을 다시 BASE64로 인코딩하여 생성한다.


JWT Debugger

JWT 공식 페이지에서는 JWT를 간단하게 디버깅 해볼 수 있는 기능을 제공한다.
이곳에서 사용 가능하다.

사이트에서 확인하면 알 수 있는 것처럼 서명에서 키 값을 변경하면 아래의 사진처럼 서명의 BASE64 값이 변경되는 것을 확인할 수 있다.


JWT를 사용할 경우…

위에서 알 수 있는 것처럼 만약 토큰 값을 누군가 수정할 경우 아래의 사진처럼 Debuuger에서 확인할 수 있는 것처럼 변조가 된 것을 확인할 수 있다.

그리고 JWT 토큰 페이로드에는 BASE64로 인코딩만 될 뿐 이것을 다시 디코딩하여 정보를 얻을 수 있다.
그래서 가급적 토큰에는 중요 정보를 담지 않는 것을 추천한다.


정리

이로써 JWT를 알아보았다.
다음 포스팅에서는 JWT를 어떤 식으로 활용하는지에 대하여 알아보도록 하겠다.


Written by@MHLab
로또는 흑우집합소 🎲
와인관리, 시음노트, 셀러관리는 마와셀 🥂

🫥 My Service|  📜 Contact|  💻 GitHub