JWT 보안: 서명 검증부터 jti (JWT ID: JSON Token Identifier) 관리
JWT(JSON Web Token)는 인증 시스템의 핵심 구성 요소로 널리 사용됩니다. 하지만 단순히 서명만 검증하고 사용하는 것만으로는 보안상 충분하지 않습니다. 이 글에서는 JWT 구조, 서명(Signature) 검증, jti (JWT ID) 검증, 실제 예시, Redis 연동, 그리고 클라이언트에서의 안전한 사용법까지 실전 위주로 모두 정리합니다.
1. JWT 구조 예시
JWT는 다음과 같이 3개의 파트로 구성된 문자열입니다:
<헤더>.<페이로드>.<서명>
예시 JWT 문자열
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiJ1c2VyMTIzIiwianRpIjoiYTIzNC1iNTY3LWQ4OWEtZmdoIiwiaWF0IjoxNzE2MjAwMDAsImV4cCI6MTcxNjIwMzYwMCwicm9sZSI6ImFkbWluIn0.
gnI4FbpyNdo7M43OsldBb4XZ9EMWHUuBh6ilAEM_0BE
Base64 디코딩 후 구성 요소
Header (헤더)
{
"alg": "HS256",
"typ": "JWT"
}
alg: 서명 알고리즘 (HMAC-SHA256)
typ: 토큰 타입 (JWT)
Payload (페이로드)
{
"sub": "user123",
"jti": "a234-b567-d89a-fgh", // JWT ID (고유 식별자)
"iat": 1716200000, // 발급 시각 (UNIX timestamp)
"exp": 1716203600, // 만료 시각 (UNIX timestamp)
"role": "admin"
}
sub: 사용자 식별자
jti: 고유 토큰 ID
iat: 발급 시각 (예: 2024-05-21 12:00:00)
exp: 만료 시각 (예: 2024-05-21 13:00:00)
role: 권한 정보
Signature (서명)
gnI4FbpyNdo7M43OsldBb4XZ9EMWHUuBh6ilAEM_0BE
위 Header + Payload를 조합한 후 비밀 키로 HMAC-SHA256 서명한 결과
토큰 위조 여부 검증에 사용
2. Signature vs jti: 왜 둘 다 필요한가?
✅ Signature 검증
항상 수행
토큰 위조 여부 확인
비밀 키로 서버가 직접 서명 재생성 → 원본 서명과 비교
Claims claims = Jwts.parser()
.setSigningKey("my-secret-key".getBytes())
.parseClaimsJws(jwt)
.getBody();
✅ jti (JWT ID: JSON Token Identifier) 검증
토큰 자체의 고유 식별자
블랙리스트, 로그아웃 처리, 재사용 탐지 등에 사용
서버는 Redis/DB에서 jti 존재 여부나 상태를 별도로 확인함
String jti = claims.getId();
if (redis.exists("blacklist:" + jti)) {
throw new TokenRevokedException();
}
3. Redis와 함께 jti 관리하기
예시: RefreshToken 발급 시
jti(UUID) 생성: a234-b567-d89a-fgh
Redis 저장
Key: refreshToken:a234-b567-d89a-fgh
Value:
{
"userId": "user123",
"ua": "Chrome/122",
"ip": "192.168.0.1",
"expiresAt": 1716800000
}
TTL: 7일
로그아웃 시 처리
해당 jti를 블랙리스트로 이동
이후 모든 요청에서 jti 확인 후 거부 처리
4. AccessToken vs RefreshToken 비교
| 항목 | AccessToken | RefreshToken |
| 역할 | 인증에 사용 | AccessToken 재발급에 사용 |
| 유효 시간 | 짧음 (5~15분) | 김 (7~30일) |
| 보관 위치 | 클라이언트(메모리, 쿠키) | 서버(Redis/DB) 또는 HttpOnly 쿠키 |
| jti 사용 여부 | 선택 (로그아웃 블랙리스트 등) | 필수 (서버가 상태 관리) |
5. 클라이언트에서 AccessToken 디코딩은 가능할까?
✅ 디코딩은 가능하지만, ❌ 신뢰해서 사용하면 안 됩니다.
안전한 사용 예
| 용도 | 허용 여부 | 이유 |
| UI에 사용자명 표시 | ✅ | 사용자 경험 개선용 |
| 관리자 UI 숨김 처리 | ✅ | 단, 표시만 가능 |
| 관리자 API 접근 제어 | ❌ | 서버에서 role 검증 필수 |
| userId로 API 호출 | ❌ | 위조 토큰일 수 있음 |
6. 최종 정리
| 검증 항목 | 필수 여부 | 설명 |
| Signature | ✅ 필수 | 위조 방지를 위해 서명 검증 |
| jti | ⭕ 권장 | 재사용 차단, 로그아웃, 토큰 무효화 등 |
서명(Signature) 은 토큰이 변조되었는지 판단
jti (JWT ID) 는 토큰이 아직 유효한지, 서버가 허용하는지 판단
보안은 두 단계 모두 필요합니다
마무리
JWT는 위조 방지를 위한 서명 검증(Signature)과
상태 추적을 위한 토큰 식별자(jti)를 함께 활용할 때,
안정적이면서 보안성 높은 인증 시스템을 만들 수 있습니다.
서명은 위조를 막고, jti는 남용을 막는다.