
들어가며
안녕하세요, 개발자 비니입니다.
웹 암호학의 시작이자, 웹 인증 시스템을 공부할 때
가장 먼저 나오는 내용 중 하나인 JWT를 들고 왔습니다.
사실 사람들은 JWT를 사용할 줄만 알지, 실제 로직을 잘 모르는 경우가 많은 것 같아요.
이 참에 뭔지 스스로 정리할 겸 기록을 남겨보고자 합니다.

JWT
Json Web Token
Json 형식으로 발급되는 웹 토큰이란 뜻입니다.
정확히 이야기하면,
정보를 비밀리에 전달하거나 인증할 때 주로 사용하는 토큰입니다.
서버와 클라이언트가 통신할 때,
인증 정보를 서로 주고받을 때 주로 사용되고 있어요.
최근에도 웹 개발을 한다고 하면, Session 방식이나 JWT 방식으로 개발을 하곤 하죠.
토큰 내용은 표준규약에 따라 생성한 토큰이에요.
중간에 탈취하더라도 복잡한 string 형태로 저장되어 읽기 힘들지만,
인코딩된 값일 뿐 암호화는 아닙니다. 민감한 정보를 직접 담지 않도록 주의해야 합니다.
택배로 이해하는 JWT
JWT를 이해하기 위해 택배를 이용 해 볼게요.
우리가 택배를 보낼 때를 생각 해 봅시다.
1. 보낼 물건을 선정합니다.
보낼 물건을 먼저 정해야겠죠.
그게 책이 되든, 음식이 되든, 도구가 되든 말이죠!
2. 보낼 물건에 맞는 박스 포장을 선택합니다.
박스는 다양한 크기가 있어요.
포장 박스의 모양도, 대표하는 브렌드도 다르죠.
3. 누가 보내는 건지 작성하고, 발송!
누가 이 택배를 보내는지에 대한 정보가 담겨있어요.
이걸 JWT에 그대로 적용 해 볼까요?
1. 보낼 물건을 선정합니다. (Payload)
JWT의 세 파트 중에는 Content 정보를 담는 "Payload" 파트가 있어요.
해당 부분에는 보낼 물건, 즉 "내용" 이 들어갑니다.
2. 보낼 물건에 맞는 박스 포장을 선택합니다. (Header)
어떤 박스로 포장할지 선택했던 것처럼,
"어떤 알고리즘으로 암호화하지?"
"어떤 토큰 방식을 사용할 거지?"
와 같은 정보를 담고 있는 파트예요.
3. 누가 보내는 건지 작성하고, 발송! (Signature)
가장 중요한 부분이에요, 자세한 내용은 아래에서 설명할게요.
이 파트는 해당 토큰이 중간에 변조되는 등의 문제를 방지하기 위한 부분이에요.
Payload와 Header를 가지고 만들어지는 암호화된 정보랍니다.
간단하게 알아보았으니,
이제 보다 더 제대로 알아볼까요?
JWT의 3요소
1. Header (헤더)
JWT의 Header는 토큰의 유형과 암호화에 사용된 알고리즘 정보를 담고 있습니다.
- typ: 토큰의 유형을 명시합니다. 일반적으로 "JWT"라는 값을 가집니다.
이 외에도, JWS라던지, JWE라던지 여러 가지가 있어요. - alg: 토큰을 서명할 때 사용하는 알고리즘을 나타냅니다. 예를 들어, HMAC SHA256을 사용한다면 "HS256"이 됩니다.
HMAC, RSA, ECDSA 등등 여러가지 알고리즘이 있답니다!
{
"typ": "JWT",
"alg": "HS256"
}
2. Payload (페이로드)
Payload는 JWT의 두 번째 부분으로, 실제 전달하고자 하는 정보를 포함합니다.
이 정보는 클레임(Claims, 주장)이라고 불리며, 사용자 및 추가 데이터를 나타낼 수 있습니다.
여기서 왜 "주장"이라 하는가?
JWT는 서버에 전달하는 역할이지, 모든 처리와 무결성 검사는 서버에서 처리합니다.
혹시라도, 위변조 되었을 확률을 모두 방지하기 위해 주장이라 해요.
- Registered Claims: 사전 정의된 클레임으로, 예를 들어 iss (발급자), exp (만료 시간), sub (주제) 등이 있습니다.
더 쉽게 말하면, 이미 정해진 정보를 담고 있는 거죠. JWT 시스템 간 표준이라 생각하면 됩니다. - Public Claims: 공개 클레임으로, 응용 프로그램에서 정의하고 사용하는 정보입니다.
말 그대로 규칙이 없는 정보를 말해요. username 이라던지, role이라던지, 개발자가 직접 정의해서 사용하는 거죠. - Private Claims: 특정 응용 프로그램 및 서비스 간에 사용되는 클레임으로, 양쪽에서 합의된 클레임입니다.
사내에서 특정 서비스끼리만 사용하도록 하는 정보로, 비공개라는 의미예요. 내부자들끼리만 이해할 수 있는 정보!
Registered Claims (사전 정의된 클레임)
{
"iss": "https://example.com", // 발급자
"exp": 1700000000, // 만료 시간
"sub": "user1" // 사용자 ID 등~
}
Public Claims (공개 클레임)
{
"username": "데비니", // 사용자 이름
"role": "admin" // 사용자 역할
}
Private Claims (비공개 클레임)
{
"internalUserId": "xyz123", // 내부 사용자 ID
"orderTrackingCode": "abc456" // 주문 추적값 등등등
}
3. Signature (시그니처)
Signature는 JWT의 세 번째 부분으로, 토큰의 무결성을 보장합니다.
이는 토큰이 생성된 후에 수정되지 않았음을 보장하기 위해 존재해요.
- 알고리즘: Header에 명시된 알고리즘을 사용합니다.
- 내용: Base64 Url로 인코딩 된 Header와 Payload를 연결하고, 비밀 키를 사용하여 서명합니다.
해당 파트에 들어가는 정보는 "자동 생성" 되도록 되어있어요.
앞의 Header, Payload 정보를 기반으로 생성하는 고유 값이기 때문에, 중간에 Payload와 Header가 탈취되어 변조된다 해도 해당 시그니처가 일치하지 않으면 서버에서 위변조 된 데이터라 인식할 수 있는 거죠.
요약
- Header: 토큰 유형과 암호화 알고리즘 정보.
- Payload: 전송하고자 하는 정보, 사용자 데이터나 클레임.
- Signature: 토큰의 무결성을 보장하는 암호화된 서명.
JWT 구성
Header & Payload & SIgnature의 융합
토큰 정보는 3개의 파트로 구분되고, 각 파트를 "."(온점)으로 연결하여 하나의 기다란 String으로 발행됩니다!
각 파트의 값은 Base64로 인코딩 되어서 붙게 되어요.
아래 상황을 가정하고, JWT 토큰을 만들어 볼까요?
[Header]
{
"typ": "JWT",
"alg": "HS256"
}
=> Base64로 인코딩한 값 : eyJ0eXAiOiAiSldUIn0.eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9
[Payload]
{
"username": "데비니",
"role": "admin"
}
=> Base64로 인코딩한 값 : eyJ1c2VybmFtZSI6ICJkZWJpbmkiLCAicm9sZSI6ICJhZG1pbiJ9
자, Header와 Payload를 이제 "."으로 이어 붙입니다.
"eyJ0eXAiOiAiSldUIn0.eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9" . "eyJ1c2VybmFtZSI6ICJkZWJpbmkiLCAicm9sZSI6ICJhZG1pbiJ9"
이제, Header에서 정의한 alg값이 HS256이니, 해당 알고리즘을 통해서 시그니처를 생성할 거예요.
생성할 때는, 가지고 있는 비밀 키 를 이용하여 서명하게 됩니다.
Signature = HMACSHA256(base64Url(Header) + "." + base64Url(Payload), "비밀키")
그리고 나온 시그니처까지 포함하여 나타나는 거대한 string값이 이제 JWT 값이 되는 거죠.
헤더 : eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9
.
페이로드 : eyJ1c2VybmFtZSI6ICJkZWJpbmkiLCAicm9sZSI6ICJhZG1pbiJ9
.
시그니처 : dGhpcyBpcyBhc3N1bWVkIHN0cmluZyBmb3Igc2lnbmF0dXJl
어때요, 간단하지 않나요?
JWT 동작 원리
발급 로직
위에서 JWT가 발급되는 방법에 대해 알아보았으니,
실제 서버와 클라이언트 간 통신이 어떤 플로우로 진행되는지 알아봅시다.
- 사용자가 id와 password를 입력하여 로그인 요청을 서버에 전송합니다.
- 서버는 사용자의 계정 정보를 확인하고, 비밀키를 통해 토큰을 발급합니다.
- 클라이언트는 해당 토큰을 전달받아 브라우저에 저장합니다.
정말 간단하지 않나요?
서버 로그인에 성공하면, 서버에서 토큰을 만들어 클라이언트로 전송합니다
인증 로직
위에서 발급한 토큰값을 클라이언트에서 들고 있습니다.
만약, "내 정보 수정"과 같은 기능을 수행해야 하는 경우는 어떤 식으로 진행될까요?
- JWT 토큰과 함께 서버로 데이터 요청 신호를 보냅니다.
- 시그니처의 무결성 여부를 검증합니다.
- 클라이언트에게 응답 신호와 데이터를 전송합니다.
이게 끝입니다!
클라이언트 쪽에서 전달받은 토큰을 저장해 두고, 서버에 요청할 때마다 서버는 토큰을 검증하는 거죠.
그다지 어려운 방식이 아니죠.
정리하자면...
JWT란 무엇인가?
- 아래 세 가지 요소가 "."으로 구분되어 저장된 string 값
- Header: 토큰 유형과 암호화 알고리즘 정보.
- Payload: 전송하고자 하는 정보, 사용자 데이터나 클레임.
- Signature: 토큰의 무결성을 보장하는 암호화된 서명.
- 사용자 인증을 위해 주로 사용되는 Json-Web-Token
JWT의 로직이 뭔가?
- 사용자의 인증이 완료된 이후, 서버는 JWT 토큰을 발급한다.
- 클라이언트쪽에서는 전달받은 토큰을 저장해 둔다.
- 클라이언트가 서버에 요청을 할 때마다 해당 토큰을 서버에 함께 전달한다.
- 서버는 토큰을 검증하고 응답한다.
마치며
최근 면접에서 해당 질문을 받은 적이 있습니다.
인증 서비스를 구현하는 사람이라면,
서버 네트워크를 다룬다면 한 번쯤은 돌아볼 가치가 있는 주제인 것 같아요.
혹시라도 또 궁금하거나 모르겠는 게 있다면,
댓글 남겨주세요!
그럼 다들, 파이팅입니다!💫
'이론' 카테고리의 다른 글
| S.O.L.I.D 객체 지향 설계 5원칙, 솔리드 원칙이란? (4) | 2025.01.20 |
|---|---|
| 비동기 프로그래밍? 동시성 제어? - with Java (1) | 2025.01.13 |
| JAVA 8/11/17/21 버전 별 변화와 LTS에 대해 (2) | 2025.01.06 |
| REST, REST API, RESTful API 본격 알아보기 (3) | 2025.01.02 |
댓글 개