Home

Published

- 14 min read

[ Web ] JWT(JSON Web Token)

img of [ Web ] JWT(JSON Web Token)

들어가며

JWT는 서비스 개발을 하게 된다면 로그인 기능 구현시 가장 먼저 생각나는 개념이다. 나도 개발할 때 가볍게 개념만 보고 써보기만 했는데, 이번 기회에 정리를 해두려고 한다.(채팅 서비스 개발하면서 클라이언트 개발자와 논의하다가 의문 + 막힘)

정리하려고 보니까 공식 문서가 잘 정리되어 있다. 번역으로 보자.

JWT(JSON Web Token)란?

JWT(JSON Web Token)는 당사자 간에 정보를 JSON 객체로 안전하게 전송하기 위한 컴팩트하고 자체 포함된 방식을 정의하는 개방형 표준(RFC 7519)이다. 이 정보는 디지털 서명되어 있기 때문에 검증되고 신뢰할 수 있다. JWT는 비밀키(HMAC 알고리즘 사용)나 공개/개인 키 쌍(RSA 또는 ECDSA 사용)을 사용하여 서명될 수 있다.

JWT는 당사자 간의 비밀성을 제공하기 위해 암호화될 수 있지만, 우리는 서명된 토큰에 초점을 맞출 것이다. 서명된 토큰은 그 안에 포함된 클레임의 무결성을 검증할 수 있는 반면, 암호화된 토큰은 이러한 클레임을 다른 당사자로부터 숨긴다. 토큰이 공개/개인 키 쌍을 사용하여 서명될 때, 서명은 또한 개인 키를 보유한 당사자만이 서명했음을 인증한다.

JWT를 언제 사용해야 하는가?

JWT가 유용한 몇 가지 시나리오는 다음과 같다.

  1. 인증: JWT의 가장 일반적인 사용 시나리오이다. 사용자가 로그인하면, 이후의 각 요청에는 JWT가 포함되어 해당 토큰으로 허용된 경로, 서비스 및 리소스에 접근할 수 있게 한다. 단일 로그인(Single Sign On)은 오버헤드가 적고 다른 도메인에서 쉽게 사용할 수 있어 요즘 JWT를 널리 사용하는 기능이다.
  2. 정보 교환: JWT는 당사자 간에 정보를 안전하게 전송하는 좋은 방법이다. JWT는 서명될 수 있기 때문에(예: 공개/개인 키 쌍 사용) 발신자가 누구인지 확실히 알 수 있다. 또한 서명이 헤더와 페이로드를 사용하여 계산되므로 내용이 변조되지 않았는지도 확인할 수 있다.

JWT의 구조는 어떻게 되는가?

JWT는 압축된 형태로 점(.)으로 구분된 세 부분으로 구성된다.

  1. Header
  2. Payload
  3. Signature

따라서 JWT는 일반적으로 다음과 같이 보인다.

   xxxxx.yyyyy.zzzzz

각 부분을 자세히 살펴보자.

Header는 일반적으로 두 부분으로 구성된다.

토큰의 유형(JWT)과 사용되는 서명 알고리즘(예: HMAC SHA256 또는 RSA).

예:

   {
	"alg": "HS256",
	"typ": "JWT"
}

이 JSON은 Base64Url로 인코딩되어 JWT의 첫 번째 부분을 형성한다.

Payload

토큰의 두 번째 부분은 클레임을 포함하는 payload이다. 클레임은 엔티티(일반적으로 사용자)에 대한 진술과 추가 데이터다. 클레임에는 등록된 클레임, 공개 클레임, 비공개 클레임 세 가지 유형이 있다.

  • 등록된 클레임: 필수는 아니지만 권장되는 미리 정의된 클레임 집합이다. 여기에는 iss(발행자), exp(만료 시간), sub(제목), aud(대상) 등이 포함된다.
  • 공개 클레임: JWT를 사용하는 사람들이 마음대로 정의할 수 있다. 그러나 충돌을 피하기 위해 IANA JSON Web Token Registry에 정의되거나 충돌 방지 네임스페이스를 포함하는 URI로 정의해야 한다.
  • 비공개 클레임: 이는 당사자 간에 정보를 공유하기 위해 생성된 사용자 지정 클레임으로, 등록된 클레임이나 공개 클레임이 아니다.

payload의 예:

   {
	"sub": "1234567890",
	"name": "John Doe",
	"admin": true
}

페이로드는 Base64Url로 인코딩되어 JWT의 두 번째 부분을 형성한다.

서명된 토큰의 경우 이 정보는 변조로부터 보호되지만 누구나 읽을 수 있다. 따라서 암호화되지 않은 한 JWT의 페이로드나 헤더 요소에 비밀 정보를 넣지 말아야 한다.

Signature

signature 부분을 만들려면 인코딩된 헤더, 인코딩된 페이로드, 비밀키, 헤더에 지정된 알고리즘을 가져와 서명해야 한다.

예를 들어, HMAC SHA256 알고리즘을 사용하려면 다음과 같이 서명이 생성된다.

   HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

서명은 메시지가 도중에 변경되지 않았음을 확인하는 데 사용되며, 개인 키로 서명된 토큰의 경우 JWT의 발신자가 주장하는 대로인지 확인할 수도 있다.

모두 합치기

출력은 점으로 구분된 세 개의 Base64-URL 문자열로, HTML 및 HTTP 환경에서 쉽게 전달할 수 있으며 SAML과 같은 XML 기반 표준에 비해 더 컴팩트하다.

다음은 이전 헤더와 페이로드가 인코딩되고 비밀키로 서명된 JWT를 보여준다.

JWT를 실제로 사용해보고 이러한 개념을 실습해보고 싶다면, jwt.io Debugger를 사용하여 JWT를 디코딩, 확인 및 생성할 수 있다.

JWT는 어떻게 작동하는가?

인증에서, 사용자가 자격 증명을 사용하여 성공적으로 로그인하면 JSON Web Token이 반환된다. 토큰은 자격 증명이므로 보안 문제를 방지하기 위해 세심한 주의를 기울여야 한다. 일반적으로 필요한 시간보다 오래 토큰을 보관해서는 안 된다.

또한 보안 부족으로 인해 브라우저 저장소에 중요한 세션 데이터를 저장해서는 안 된다.

사용자가 보호된 경로나 리소스에 접근하려 할 때마다, 사용자 에이전트는 일반적으로 Bearer 스키마를 사용하여 Authorization 헤더에 JWT를 보내야 한다. 헤더의 내용은 다음과 같아야 한다:

   Authorization: Bearer <token>

이는 특정 경우에 상태 비저장 인증 메커니즘이 될 수 있다. 서버의 보호된 경로는 Authorization 헤더에서 유효한 JWT를 확인하고, 존재하면 사용자는 보호된 리소스에 접근할 수 있다. JWT에 필요한 데이터가 포함되어 있다면 특정 작업에 대해 데이터베이스를 쿼리할 필요가 줄어들 수 있지만, 항상 그런 것은 아니다.

HTTP 헤더를 통해 JWT 토큰을 보내는 경우, 토큰이 너무 커지지 않도록 주의해야 한다. 일부 서버는 8KB 이상의 헤더를 허용하지 않는다. 사용자의 모든 권한을 포함하는 등 JWT 토큰에 너무 많은 정보를 포함하려고 하면 Auth0 Fine-Grained Authorization과 같은 대안 솔루션이 필요할 수 있다.

토큰이 Authorization 헤더로 전송되면 쿠키를 사용하지 않으므로 Cross-Origin Resource Sharing (CORS)는 문제가 되지 않는다.

다음 다이어그램은 JWT가 어떻게 얻어지고 API나 리소스에 접근하는 데 사용되는지 보여준다.

  1. 애플리케이션 또는 클라이언트가 인증 서버에 인증을 요청한다. 이는 다른 인증 흐름 중 하나를 통해 수행된다. 예를 들어, 일반적인 OpenID Connect 준수 웹 애플리케이션은 인증 코드 흐름을 사용하여 /oauth/authorize 엔드포인트를 통과한다.
  2. 인증이 허가되면, 인증 서버는 애플리케이션에 액세스 토큰을 반환한다.
  3. 애플리케이션은 액세스 토큰을 사용하여 보호된 리소스(예: API)에 접근한다.

서명된 토큰의 경우, 토큰 내에 포함된 모든 정보는 사용자나 다른 당사자가 변경할 수 없더라도 노출된다는 점에 유의해야 한다. 이는 토큰 내에 비밀 정보를 넣지 말아야 한다는 것을 의미한다.

왜 JWT를 사용해야 하는가?

JWT를 Simple Web Tokens (SWT) 및 Security Assertion Markup Language Tokens (SAML)와 비교했을 때의 이점에 대해 이야기해보자.

JSON은 XML보다 덜 장황하기 때문에 인코딩될 때 크기도 더 작아 JWT가 SAML보다 더 컴팩트하다. 이는 JWT를 HTML 및 HTTP 환경에서 전달하기에 좋은 선택으로 만든다.

보안 측면에서, SWT는 HMAC 알고리즘을 사용하는 공유 비밀로만 대칭적으로 서명될 수 있다. 그러나 JWT와 SAML 토큰은 X.509 인증서 형태의 공개/개인 키 쌍을 사용하여 서명할 수 있다. XML에 XML Digital Signature로 서명하는 것은 JSON에 서명하는 것의 단순함에 비해 모호한 보안 구멍을 도입하지 않고 수행하기가 매우 어렵다.

JSON 파서는 대부분의 프로그래밍 언어에서 일반적이다. 왜냐하면 그들이 객체에 직접 매핑되기 때문이다. 반대로 XML은 자연스러운 문서-객체 매핑이 없다. 이는 JWT를 SAML 어설션보다 더 쉽게 작업할 수 있게 만든다.

사용 측면에서, JWT는 인터넷 규모로 사용된다. 이는 여러 플랫폼, 특히 모바일에서 JSON 웹 토큰의 클라이언트 측 처리의 용이성을 강조한다.

Getting deeper : JWT / JWS / JWE

JWT는 스스로 존재할 수 없는 abstract class 같은 개념이다. 실제 구현은 JWS / JWE로 이루어진다. 더 자세하게 작성할 생각으로 찾아봤지만, 보안쪽으로 갈 것이 아니라면 이정도에서 마무리하는 게 좋아보인다.

간단하게 정리하자면, JWT는 JWS로 디지털 서명을 하고, JWE로 암호화하여 무결성, 기밀성을 보장한다 라고 이해하면 될 듯 하다.

Reference

참고해볼만한 글