JWT (JSON Web Tokens) 마스터하기: 심층 분석
Olivia Novak
Dev Intern · Leapcell

JSON Web Token (약어 JWT)은 현재 가장 인기 있는 크로스 도메인 인증 솔루션입니다. 이 문서에서는 그 원리와 사용법을 소개합니다.
I. 크로스 도메인 인증 문제
인터넷 서비스는 사용자 인증과 떼려야 뗄 수 없습니다. 일반적인 과정은 다음과 같습니다.
- 사용자가 사용자 이름과 비밀번호를 서버에 보냅니다.
- 서버가 성공적으로 확인한 후 사용자 역할, 로그인 시간 등과 같은 관련 데이터를 현재 세션에 저장합니다.
- 서버는 session_id를 사용자에게 반환하고 사용자 쿠키에 기록합니다.
- 사용자의 이후 각 요청에 대해 session_id는 쿠키를 통해 서버로 다시 전송됩니다.
- 서버는 session_id를 수신한 후 미리 저장된 데이터를 찾아 사용자의 신원을 알 수 있습니다. 이 모델의 문제는 확장성이 떨어진다는 것입니다. 단일 머신에는 문제가 없습니다. 그러나 서버 클러스터 또는 크로스 도메인 서비스 지향 아키텍처인 경우 세션 데이터 공유가 필요하며 각 서버는 세션을 읽을 수 있어야 합니다. 예를 들어 웹사이트 A와 웹사이트 B는 동일한 회사의 관련 서비스입니다. 이제 사용자가 웹사이트 중 하나에 로그인하면 다른 웹사이트에 액세스할 때 자동으로 로그인해야 합니다. 이를 어떻게 달성할 수 있을까요? 한 가지 해결책은 세션 데이터를 영구적으로 저장하고 데이터베이스 또는 다른 영구 계층에 기록하는 것입니다. 요청을 받은 후 다양한 서비스가 영구 계층에서 데이터를 요청합니다. 이 솔루션의 장점은 아키텍처가 명확하다는 것이지만 단점은 작업량이 비교적 많다는 것입니다. 또한 영구 계층에 오류가 발생하면 단일 지점 오류가 발생합니다. 또 다른 해결책은 서버가 세션 데이터를 저장하지 않는 것입니다. 모든 데이터는 클라이언트에 저장되어 각 요청과 함께 서버로 다시 전송됩니다. JWT는 이 솔루션의 대표적인 예입니다.
II. JWT의 원리
JWT의 원리는 서버가 인증 후 JSON 객체를 생성하여 다음과 같이 사용자에게 다시 보내는 것입니다.
{"name": "Alice", "role": "admin", "expiration time": "0:00, July 1, 2024"}
그 후 사용자가 서버와 통신할 때 이 JSON 객체를 다시 보내야 합니다. 서버는 이 객체를 기반으로 사용자의 ID를 완전히 결정합니다. 사용자가 데이터를 변조하는 것을 방지하기 위해 서버는 이 객체를 생성할 때 서명을 추가합니다 (자세한 내용은 나중에 설명합니다). 서버는 더 이상 세션 데이터를 저장하지 않습니다. 즉 서버가 상태 비저장 상태가 되어 확장이 더 쉬워집니다.
III. JWT의 데이터 구조
실제 JWT는 다음과 같습니다.
매우 긴 문자열이며 점(.)으로 세 부분으로 나뉩니다. JWT 내부에는 줄 바꿈이 없습니다. 여기서는 쉽게 표시하기 위해 여러 줄로 작성되었습니다. JWT의 세 부분은 다음과 같습니다.
- 헤더 (Header)
- 페이로드 (Payload)
- 서명 (Signature)
한 줄로 쓰면 다음과 같습니다.
Header.Payload.Signature
다음은 이 세 부분을 차례로 소개합니다.
3.1 헤더
헤더 부분은 JWT의 메타데이터를 설명하는 JSON 객체이며 일반적으로 다음과 같습니다. {"alg": "HS256", "typ": "JWT"} 위의 코드에서 alg 속성은 서명 알고리즘(algorithm)을 나타내고 기본값은 HMAC SHA256(HS256으로 작성)입니다. typ 속성은 이 토큰의 유형을 나타내고 JWT 토큰은 균일하게 JWT로 작성됩니다. 마지막으로 위의 JSON 객체는 Base64URL 알고리즘을 사용하여 문자열로 변환됩니다 (자세한 내용은 나중에 설명합니다).
3.2 페이로드
페이로드 부분은 실제로 전송해야 하는 데이터를 저장하는 데 사용되는 JSON 객체이기도 합니다. JWT는 선택을 위해 7개의 공식 필드를 정의합니다.
- iss (issuer): 발급자
- exp (expiration time): 만료 시간
- sub (subject): 주체
- aud (audience): 대상
- nbf (Not Before): 유효 시간
- iat (Issued At): 발급 시간
- jti (JWT ID): 일련 번호 공식 필드 외에도 이 부분에 개인 필드를 정의할 수도 있습니다. 다음은 예시입니다.
{"sub": "1234567890", "name": "John Doe", "admin": true}
JWT는 기본적으로 암호화되지 않으며 누구나 읽을 수 있습니다. 따라서 이 부분에 비밀 정보를 넣지 마십시오. 이 JSON 객체도 Base64URL 알고리즘을 사용하여 문자열로 변환해야 합니다.
3.3 서명
서명 부분은 데이터 변조를 방지하기 위해 처음 두 부분의 서명입니다. 먼저 비밀 키(secret)를 지정해야 합니다. 이 키는 서버만 알고 있으며 사용자에게 유출될 수 없습니다. 그런 다음 헤더에 지정된 서명 알고리즘(기본값은 HMAC SHA256)을 사용하여 다음 공식에 따라 서명을 생성합니다.
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
서명을 계산한 후 헤더, 페이로드 및 서명 부분을 문자열로 결합하고 각 부분 사이에 "점"(.)으로 구분한 다음 사용자에게 반환할 수 있습니다.
3.4 Base64URL
앞서 언급했듯이 헤더와 페이로드의 직렬화 알고리즘은 Base64URL입니다. 이 알고리즘은 기본적으로 Base64 알고리즘과 유사하지만 몇 가지 작은 차이점이 있습니다. 토큰으로서 JWT는 일부 경우에 URL에 배치될 수 있습니다 (예: api.example.com/?token = xxx). Base64에는 URL에서 특별한 의미를 갖는 세 가지 문자 +, / 및 =가 있으므로 이를 바꿔야 합니다. =는 생략되고 +는 -로 바뀌고 /는 _로 바뀝니다. 이것이 Base64URL 알고리즘입니다.
IV. JWT의 사용법
서버에서 반환된 JWT를 수신한 후 클라이언트는 쿠키 또는 localStorage에 저장할 수 있습니다. 이후 클라이언트가 서버와 통신할 때마다 이 JWT를 가져와야 합니다. 쿠키에 넣고 자동으로 보낼 수 있지만 이는 크로스 도메인이 될 수 없습니다. 따라서 더 나은 방법은 HTTP 요청 헤더의 Authorization 필드에 넣는 것입니다.
Authorization: Bearer <token>
또 다른 방법은 크로스 도메인의 경우 JWT가 POST 요청의 데이터 본문에 배치되는 것입니다.
V. JWT의 몇 가지 특징
(1) JWT는 기본적으로 암호화되지 않지만 암호화할 수 있습니다. 원래 토큰을 생성한 후 비밀 키로 다시 암호화할 수 있습니다.
(2) JWT가 암호화되지 않은 경우 비밀 데이터를 JWT에 쓸 수 없습니다.
(3) JWT는 인증뿐만 아니라 정보를 교환하는 데에도 사용할 수 있습니다. JWT를 효과적으로 사용하면 서버가 데이터베이스를 쿼리하는 횟수를 줄일 수 있습니다.
(4) JWT의 가장 큰 단점은 서버가 세션 상태를 저장하지 않기 때문에 사용 중 특정 토큰을 취소하거나 토큰의 권한을 변경할 수 없다는 것입니다. 즉 JWT가 발급되면 서버가 추가 로직을 배포하지 않는 한 만료될 때까지 유효합니다.
(5) JWT 자체에 인증 정보가 포함되어 있습니다. 유출되면 누구나 토큰의 모든 권한을 얻을 수 있습니다. 도난을 줄이기 위해 JWT의 유효 기간을 비교적 짧게 설정해야 합니다. 더 중요한 권한의 경우 사용 시 사용자를 다시 인증해야 합니다.
(6) 도난을 줄이기 위해 JWT는 HTTP 프로토콜을 사용하여 일반 텍스트로 전송해서는 안 되며 HTTPS 프로토콜을 사용하여 전송해야 합니다.
Leapcell: 웹 호스팅을 위한 최고의 서버리스 플랫폼
마지막으로 웹 서비스 배포에 가장 적합한 플랫폼인 Leapcell을 추천합니다.
1. 다국어 지원
- JavaScript, Python, Go 또는 Rust로 개발하십시오.
2. 무제한 프로젝트 무료로 배포
- 사용량에 대해서만 지불하십시오. 요청도 없고 요금도 없습니다.
3. 탁월한 비용 효율성
- 유휴 요금 없이 사용한 만큼 지불하십시오.
- 예: $25는 평균 응답 시간 60ms에서 694만 건의 요청을 지원합니다.
4. 간소화된 개발자 경험
- 손쉬운 설정을 위한 직관적인 UI.
- 완전 자동화된 CI/CD 파이프라인 및 GitOps 통합.
- 실행 가능한 통찰력을 위한 실시간 메트릭 및 로깅.
5. 간편한 확장성 및 고성능
- 쉬운 동시성 처리를 위한 자동 크기 조정.
- 운영 오버헤드가 제로입니다. 구축에만 집중하십시오.
Leapcell Twitter: https://x.com/LeapcellHQ