JWT (JSON Web Token)
📌 정의
클라이언트와 서버 사이에서 인증 및 권한 정보를 안전하게 전달하기 위한 JSON 형식의 암호화된 문자열
📢 주로 인증, 권한 부여, 정보 교환을 위해 사용함
JWT는 공유될 필요가 있는 정보를 가진 JSON 객체들을 포함한다. 각 JWT는 malicious party 또는 client에 의해 JSON 내용이 변경되지 않는 것을 보장하기 위해 암호화를 사용하여 서명받는다.
📌 JWT를 사용하는 이유
인증 서버가 Plain JSON으로서 데이터를 보낸다면 Client Application API는 올바른 정보를 받은 것인지 검증할 방법이 없다. 예를 들어 malicious attacker가 user ID를 변조할 수 있는데, 그때 Client Application API는 어떤 일이 일어났는지 알 방법이 없다.
이러한 이유로 인증 서버는 Client Application에 의해 검증될 수 있는 방식으로 정보를 보내고, 여기서 "token"의 개념이 등장한다.
🔍 Token
보안적으로 검증될 수 있는 정보를 포함하는 String
이것은 Database 내 ID를 지칭하는 문자와 숫자가 섞인 random set이 될 수도 있고, client (known as JWTs)가 자체적으로 검증할 수 있는 encoded JSON일 수도 있다.
📌 JWT 구조
JWT는 Header, Payload, Signature 3 부분으로 이루어지며, JSON 형태인 각 부분은 Base64로 인코딩 되어 표현된다. 각 부분을 구분하기 위해 '.' 구분자를 사용하여 구분한다.
Base64는 암호화된 문자열이 아니고, 같은 문자열에 대해 항상 같은 인코딩 문자열을 반환한다.
🔍 Header
헤더는 두 가지 정보를 갖고 있다.
- alg: Signature 해싱 알고리즘
- 해싱 알고리즘은 보통 HMAC-SHA256 or RSA가 사용되며, 토큰을 검증할 때 사용되는 signature 부분에서 사용된다.
- typ: 토큰의 유형으로 대부분 "JWT"다.
🔍 Payload
JSON 또는 claims를 포함되며, 토큰에 담을 정보를 갖고 있다.
📢 claim: Payload에 담는 정보 한 조각을 의미하며, JSON 형태의 한 쌍으로 이루어져 있음
🔍 Signature
JSON Payload의 무결성을 검증하거나 인코딩을 위해 사용되는 암호화 알고리즘을 통해 만들어진 String
Header와 Payload의 값을 각각 Base64로 인코딩하고, 인코딩한 값을 비밀키를 이용해 Header에서 정의한 알고리즘으로 해싱을 하고, 이 값을 다시 Base64로 인코딩하여 생성한다.
생성된 토큰은 HTTP 통신을 할 때 Authorization이라는 key의 value로 사용된다. 일반적으로 value에는 'Bearer'가 앞에 붙여진다.
{
"Authorization": "Bearer {생성된 토큰 값}",
}
📌 JWT claim convention
서비스에서 필요한 정보가 아닌 토큰에 대한 정보들을 담기 위하여 이름이 이미 정해진 claims (optional)
🎯 iss
토큰 발급자(issuer) (ex. Google)
🎯 azp and aud
토큰 대상자(audience)
application을 위해 issuer에 의해 발급된 Client IDs. 이러한 방식으로 issuer(ex. Google)은 어떤 웹 사이트가 서비스 중에 자신의 서명을 사용하려고 하는지 알 수 있다.
🎯 sub
토큰 제목(subject)
The end user's Google user ID
🎯 exp
토큰의 만료시간(expiration)
시간은 NumericDate 형식(ex. 1480849147370)으로 되어 있어야 하며, 언제나 현재 시간보다 이후로 설정되어 있어야 한다.
🎯 nbf
"Not Beform"을 의미하며, 토큰의 활성 날짜와 비슷한 개념
NumericDate 형식으로 날짜 지정, 해당 날짜가 지나기 전까지는 토큰이 처리되지 않는다.
🎯 iat
토큰이 생성(발급)된 시간(issued at)
🎯 at_hash
access token의 hash
OAuth access token은 opaque token이라는 점에서 JWT와 다르다. Access token의 목적은 client application이 로그인한 유저에 대해 더 많은 정보를 Google에게 요구하기 위한 것이다.
The end user's email ID
🎯 email_verified
유저의 이메일이 검증된 것인지 아닌지 여부
🎯 jti
JWT의 고유 식별자로서, 주로 중복적인 처리를 방지하기 위하여 사용된다. 일회용 토큰에 사용하면 유용하다.
📌 JWT의 장점
🔍 Secure
JWT는 client or attacker로부터 데이터가 변조되지 않기 위한 안전 장치로 HMAC or public/private key pair(RSA or ECDSA)를 사용하여 서명된다.
🔍 Self-Contained(자가 수용적)
JWT는 필요한 모든 정보를 자체적으로 지니고 있어 별도의 인증 저장소가 필요 없다. 또한 모든 필수 정보를 자체적으로 갖고 있기 때문에 Database로 이동할 필요가 줄어들고, Database에 이러한 인증 정보를 쿼리 할 필요가 없다.
🔍 Compact
JWT는 가볍다. 그래서 URL, POST parameter, or HTTP header를 통해 전송될 수 있다.
트래픽에 대한 부담이 낮다.
🔍 Stateless
JWT는 Stateless(무상태) authentication mechanism이므로, 사용자 상태가 Database에 저장되지 않는다.
🔍 More granular access model
JWT는 시간뿐만 아니라 범위(클라이언트가 할 수 있는 작업)에 제한을 둘 수 있기 때문에 쿠키보다 더 세분화된 액세스 모델을 제공한다.
🔍 Delegation
사용자를 대신하여 작동하는 Client에게 액세스 권한을 위임하는 데 사용될 수 있다.
📌 JWT의 단점
🔍 Stateless
JWT는 Stateless(무상태) authentication mechanism이므로, 제어가 불가능하다. 그래서 모든 devices로부터 로그아웃을 구현하기 어렵고 임의로 토큰을 삭제할 수 없어 토큰 만료 시간을 꼭 넣어주어야 한다.
🔍 Self-Contained
토큰 자체에 정보를 담고 있어 양날의 검이 될 수 있다.
🔍 Length of token
토큰의 Payload에 3종류의 claim을 저장하기 때문에 정보가 많아질수록 토큰의 길이가 늘어나 네트워크에 부하를 줄 수 있다.
🔍 Base64
Payload 자체는 암호화된 것이 아니라 Base64를 통해 인코딩되어 있다. 중간에 이를 탈취하여 디코딩하면 원본을 볼 수 있으므로, JWE로 암호화하거나 Payload에 중요 데이터를 넣지 않아야 한다.
🔍 Tore Token
토큰은 Client 측에서 관리해야 하기 때문에 토큰을 저장해야 한다.
📌 동작방식
1. 서버는 사용자의 인증 정보를 사용(검증)하여 JWT를 생성한 후 Client로 보냄
2. Client는 JWT를 서버에 전송
- 처음 인증을 받은 후 Client는 JWT 토큰을 저장하게 된다.
- Client는 다음 요청 때 Authorization 요청 헤더에 토큰을 넣어서 전송한다.
3. 서버는 JWT의 Signature를 사용하여 무결성을 확인
4. 서버는 JWT의 내용을 사용하여 사용자의 인증 상태를 확인하거나 권한을 부여한다.
References:
https://supertokens.com/blog/what-is-jwt#
https://www.youtube.com/watch?v=khniEN0PYXo&list=PL4C2AmBC9jOZSDU5D2kNWNaMv6IW3U4ll&index=1