JSON 웹 토큰(JWT)이란 무엇인가요?
5분 만에 JSON 웹 토큰(JWT) 기본 사항에 대해 명확하게 이해하십시오.
JSON 웹 토큰(JWT)은 현대 웹 응용 프로그램과 OpenID Connect와 같은 개방형 표준에서 널리 사용되며, 인증 및 권한 부여를 용이하게 합니다. 공식 RFC 7519는 필수적인 참조 자료로서의 역할을 하지만, 초보자들이 이해하기는 어렵습니다. 이 글에서는 JWT의 핵심 개념에 초점을 맞추고, 이해하기 쉬운 언어로 예시를 들어 설명하겠습니다.
왜 JWT가 필요한가요?
오늘날, 두 당사자 간에 데이터를 교환하기 위해 JSON을 사용하는 것이 상당히 일반적입니다. 사용자를 나타내는 JSON 객체를 생각해 보십시오:
sub
는 "subject"의 줄임말로, 사용자 식별자(user ID)를 나타내기 위해 OpenID Connect의 표준 요구 사항입니다.
이 JSON 객체의 무결성을 어떻게 보장할 수 있을까요? 다시 말해, 전송 중에 데이터가 조작되지 않았다는 것을 어떻게 확인할 수 있을까요? 일반적인 해결책은 디지털 서명을 사용하는 것입니다. 예를 들어, 서버가 개인 키로 JSON 객체를 서명하고 클라이언트는 서버의 공개 키로 서명을 검증할 수 있게 공개키 암호화를 사용할 수 있습니다.
간단히 말하면, JWT는 JSON 객체와 그 서명을 표현하는 표준화된 방법을 제공합니다.
JWT의 형식
디지털 서명을 생성하기 위한 많은 알고리즘들이 있으므로, JWT 서명에 사용된 알고리즘을 지정해야 합니다. 이는 JSON 객체를 구성함으로써 이루어집니다:
alg
은 "algorithm"의 줄임말이며,typ
은 "type"의 줄임말입니다.
일반적으로, typ
은 대문자 JWT
로 설정됩니다. 우리의 예제에서, alg
는 HS256
이며, 이는 HMAC-SHA256을 의미하고(곧 설명하겠습니다), 이 알고리즘을 사용하여 서명을 생성한다는 것을 나타냅니다.
이제 JWT를 위한 모든 재료를 갖추었습니다:
- 헤더 JSON: 알고리즘과 유형
- 페이로드 JSON: 실제 데이터
- 서명: 헤더와 페이로드를 포함하는 서명
하지만, 공백과 줄 바꿈 같은 특정 문자들은 네트워크 전송에 친숙하지 않습니다. 따라서, 헤더와 페이로드는 Base64URL로 인코딩되어야 합니다. 전형적인 JWT는 이렇게 생겼습니다:
.
은 구분자로 사용됩니다.
모든 것을 함께 모아서 JWT를 생성해 봅시다:
헤더
JSON: {"alg":"HS256","typ":"JWT"}
Base64URL 인코딩: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
페이로드
JSON: {"sub":"foo","name":"John Doe"}
Base64URL 인코딩: eyJzdWIiOiJmb28iLCJuYW1lIjoiSm9obiBEb2UifQ
서명
HMAC-SHA256에서, 서명은 다음과 같은 비밀로 생성됩니다:
예를 들어, 비밀이 some-great-secret
인 경우, 서명은 다음과 같이 됩니다: XM-XSs2Lmp76IcTQ7tVdFcZzN4W_WcoKMNANp925Q9g
.
JWT
최종 JWT는 다음과 같습니다:
이 유효한 JWT는 비밀을 소지한 어떤 당사자든지 검증할 수 있습니다.
서명 알고리즘 선택
앞서 언급한 바와 같이, 디지털 서명을 생성하는 데에는 다양한 알고리즘이 있습니다. 우리는 예시로 HS256
을 사용했지만, 이는 비밀이 당사자들 (예. 클라이언트와 서버)간에 공유되어야 하므로 충분히 강력하지 않을 수 있습니다.
실제로, 클라이언트는 React 앱과 같은 공개 응용 프로그램을 포함할 수 있으며, 이러한 앱은 비밀을 안전하게 보호할 수 없습니다. 따라서, 선호되는 접근 방식은 공개키 암호화 (즉, 비대칭 암호화)를 사용하여 JWT 에 서명하는 것입니다. 가장 인기 있는 알고리즘인 RSA부터 시작해 봅시다.
RSA
RSA, 즉 비대칭 알고리즘이라 하면, 키 쌍을 사용합니다: 공개 키와 개인 키입니다. 공개 키는 서명을 검증하는 데 사용되며, 개인 키는 서명하는 데 사용됩니다.
RSA를 위한 헤더 JSON은 아래와 같습니다:
RS256
은 RSA-SHA256을 의미하며, 이는 RSA 알고리즘과 SHA256 해시 함수를 사용하여 서명이 생성된다는 것을 의미합니다. 또한 SHA384와 SHA512 해시 함수를 사용하여 서명을 생성하기 위해RS384
과RS512
를 사용할 수도 있습니다.
서명은 개인 키로 생성됩니다:
마찬가지로, 이 부분들을 조립하여 JWT를 생성할 수 있으며, 최종 JWT는 아래와 같습니다:
이제, 클라이언트는 개인 키를 알지 못해도 서명을 검증할 수 있습니다.
ECDSA
RSA가 널리 사용되고 있지만, 그것은 큰 서명 크기를 가지고 있으며, 때때로 헤더와 페이로드의 합계보다 더 클 수 있습니다. 타원 곡선 디지털 서명 알고리즘(ECDSA)은 더 작은 크기의 서명을 생성할 수 있는 또 다른 비대칭 알고리즘으로, 성능이 더 좋습니다.
ECDSA의 개인 키를 생성하려면, 곡선을 선택해야 합니다. 이는 이 글의 범위를 벗어나지만, 당신은 여기에서 더 많은 정보를 찾을 수 있습니다.
ECDSA를 위한 헤더 JSON은 아래와 같습니다:
ES256
은 ECDSA-SHA256을 의미하며, 이는 ECDSA 알고리즘과 SHA256 해시 함수를 사용하여 서명이 생성된다는 것을 의미합니다. 당신은 또한 SHA384와 SHA512 해시 함수를 사용하여 서명을 생성하기 위해ES384
과ES512
를 사용할 수도 있습니다.
서명은 개인 키로 생성됩니다:
최종 JWT은 RSA와 구조는 같지만 서명은 훨씬 더 짧습니다:
JWT 검증하기
JWT를 검증하는 것은 JWT를 작성하는 것의 역과정만큼이나 단순합니다:
.
구분자를 사용하여 JWT를 세 부분(헤더, 페이로드, 서명)으로 분할합니다.- Base64URL로 헤더와 페이로드를 디코딩합니다.
- 헤더에서 지정된 알고리즘과 공개 키(비대칭 알고리즘의 경우)로 서명을 검증합니다.
JWT 검증을 돕기 위한 많은 라이브러리들이 있습니다, 예로 Node.js와 웹 브라우저를 위한 jose 등이 있습니다.
결론
이 글에서는, JWT의 핵심 개념들과, 그것을 어떻게 생성하고 검증하는지에 대한 개요를 간략히 설명했습니다. 많은 세부 사항들이 아직 탐색되지 않았으며, 우리는 그것들을 미래의 글들에서 다룰 것입니다.
Logto는 JWT와 OpenID Connect와 같은 개방형 표준을 활용하여 개발자들의 간단한 워크플로우를 통해 앱과 API를 보호합니다. 관심이 있으시다면, 무료로 시도해 볼 수 있습니다 (신용 카드 필요 없음).