JWT 기반 인증의 그림자: 편리함 뒤에 숨은 치명적 위협

JWT 기반 인증의 그림자: 편리함 뒤에 숨은 치명적 위협

개요

현대 웹 애플리케이션과 모바일 앱의 표준으로 자리 잡은 JWT는 stateless 인증의 편리함을 제공하지만, 안전하지 않게 운영 및 관리되는 경우 인증 체계 전체를 붕괴시킬 수 있는 단일 실패 지점(Single Point of Failure)이 될 수도 있다.
본 글은 JWT 개념과 인증 방식을 소개한 뒤, CVE 사례 중심으로 주요 취약점을 분석하고 이를 예방 및 완화하기 위한 실질적인 방어 전략을 제시한다.

 

JSON Web Token (JWT)

JWT는 웹 표준(RFC 7519)으로, JSON 객체를 사용하여 두 개체 간에 정보를 안전하게 전송하는 가볍고 자가-수용적인(compact and self-contained) 방식이다. 이 토큰은 모든 필수 정보를 자체적으로 담고 있어 서버에서 데이터베이스 조회 없이 토큰만으로 사용자 인증 및 권한 확인이 가능하다. 또한 무상태(stateless) 구조로 서버 측 세션의 저장 및 동기화 부담을 줄이고, HTTP 헤더나 URL 파라미터로 손쉽게 전달되어 분산 환경에서 확장과 전달이 용이하다는 점으로 인해 JWT는 현대 웹/마이크로서비스 환경의 핵심 인증 방식으로 자리 잡았다.

 

1) JWT의 구조 및 구성 요소

JWT는 Base64Url로 인코딩 된 세 부분(Header, Payload, Signature)이 마침표(.)로 구분되어 결합된 형태로 구성된다.

[그림 1] JWT 구조

 

1. Header (헤더) : 메타 정보

헤더는 토큰의 메타 정보를 담고 있는 JSON 객체를 Base64Url로 인코딩한 부분이다. 필수적으로 typ (Type, 일반적으로 JWT) 및 alg (Algorithm, 서명에 사용된 암호화 알고리즘, 예: HS256, RS256) 클레임을 포함한다.

{
  "alg": "HS256",  // 서명 알고리즘
  "typ": "JWT"     // 토큰 타입
}

 

2. Payload (페이로드) : 클레임(Claim)

페이로드에는 전송하고자 하는 인증 정보, 권한 정보, 기타 속성 정보가 JSON 객체 형태로 포함되며 이를 클레임(Claim)이라고 부른다. 페이로드 역시 헤더와 마찬가지로 Base64Url로 인코딩된다.

  • 클레임의 종류
    클레임은 역할에 따라 Registered (표준 정의), Public (공개), Private (사설) 세 종류로 나뉜다. Registered Claims에는 토큰의 만료 시점(exp), 발급 시점(iat), 발급자(iss), 수신자(aud) 등 토큰의 유효성 검증에 활용되는 표준 필드를 포함한다.
  • 보안 유의점
    헤더와 페이로드는 암호화가 아닌 단순 인코딩이므로, 페이로드를 확보하면 손쉽게 디코딩하여 내용 파악이 가능하다. 따라서 페이로드에는 이메일, 결제 정보 등 민감한 개인 식별 정보(PII)를 포함해서는 안 된다.
{
  "sub": "user123",           // 사용자 ID
  "name": "John Doe",         // 사용자 이름
  "role": "admin",            // 권한
  "iat": 1516239022,          // 발급 시각
  "exp": 1516242622           // 만료 시각
}

 

3. Signature (서명) : 무결성 보장

서명은 토큰의 무결성(Integrity) 및 신뢰성(Authenticity)을 보장하는 핵심 요소다. 서명은 인코딩된 Header와 Payload, 그리고 서버가 보관하는 Secret Key(HS256) 또는 Private Key(RS256)를 결합해 지정된 알고리즘에 따른 암호학적 서명 연산으로 계산된다. 서버는 서명을 재계산하여 수신된 서명과 일치하는지 확인하며, 이를 통해 토큰의 위변조 여부를 판단한다. 비밀 서명 키를 모르는 공격자는 유효한 서명을 생성할 수 없다.

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

 

2) 세션 기반 인증 방식과 JWT 인증 방식의 비교

세션 기반 인증 방식과 JWT 인증 방식의 차이는 [그림 2]의 도식 비교를 통해 확인할 수 있다.

[그림 2] 세션 기반 인증 방식과 JWT 인증 방식 과정 비교

 

세션 기반 인증 방식

세션 기반의 인증 방식은 크게 네 가지의 단점을 지닌다.

  • 서버가 세션 상태를 직접 관리해야 하는 Stateful 구조
  • 서버 수평 확장 시 세션 공유(공동 세션 스토어, Sticky Session 등)가 필요
  • 세션 저장소(DB, Redis 등)에 대한 추가 부하 및 운영 비용 발생
  • 중앙 세션 저장소에 의존하기 때문에 마이크로서비스 및 분산 아키텍처에서 유연성이 떨어질 수 있음

 

JWT 인증 방식

반면 JWT 인증 방식은 세션 기반 인증의 단점을 어느 정도 해소한다.

  • Stateless 구조로 별도의 세션 저장소 없이 서명 검증만으로 기본 인증이 가능
  • 서버/인스턴스 간 세션 동기화가 필요 없어 수평 확장에 유리
  • 각 서비스가 토큰을 직접 검증할 수 있어 마이크로서비스 아키텍처에 친화적
  • 토큰에 사용자 식별자, 권한, 만료 시간 등을 포함하여 서버 상태에 대한 의존도를 줄임

 

주요 JWT 보안 취약점

JWT의 취약점은 주로 서명 검증 로직의 구현 오류나, 사용자가 조작 가능한 Header 파라미터를 서버가 무분별하게 신뢰할 때 발생한다. JWT의 핵심 장점인 Stateless 특성은 운영 설계가 미흡할 경우 역설적으로 치명적인 약점으로 작용할 수 있다. 전통적인 세션 방식은 서버에서 세션을 삭제할 경우 강제 로그아웃이 즉시 반영되지만, JWT는 클라이언트가 보관하므로 서버가 토큰을 즉시 회수하기 어렵고 별도의 철회 메커니즘이 없다면 만료 시점까지 유효할 수 있다. 특히 직원의 권한이 변경되거나 계정이 비활성화되더라도 이전에 발급된 JWT가 만료 전이라면 API 접근이 가능할 수 있다. 계정 탈취를 탐지해 비밀번호를 재설정하더라도 공격자가 이미 확보한 JWT는 만료 전까지 유효하게 남을 수 있다.

서명 키가 유출되면 공격자는 임의의 사용자로 가장하거나 권한을 상승시킨 토큰을 생성할 수 있다. 실제로 탈취된 서명 키를 이용해 인증 토큰을 위조하고 대량의 데이터 유출이 발생한 사례가 보고된 바 있다. 이처럼 위조 토큰이 서명 검증을 통과하면 서버 및 탐지 시스템은 이를 정상 요청으로 인식하고 처리할 수 있어, 탐지가 지연되는 상황이 발생할 수 있다.

 

1) 서명 무결성 검증 우회 취약점

1. HS/RS 알고리즘 혼동 취약점 (CVE-2015-9235)

jsonwebtoken Node.js 4.2.2 미만 버전은 비대칭 알고리즘(RS/ES 계열)로 검증해야 하는 상황에서도 대칭 알고리즘(HS 계열)로 서명된 토큰을 받아들이는 알고리즘 혼동 문제가 존재한다. 공격자는 서버가 검증에 사용하는 공개키를 HMAC 비밀키처럼 악용해 임의의 페이로드로 토큰을 위조하고 인증을 우회하거나 권한을 상승시킬 수 있다.

2. alg=none 허용 취약점 (CVE-2021-22160 / CVE-2022-23540 / CVE-2025-61152)

일부 구현은 alg: “none”이 포함된 토큰에 대해 서명 검증을 수행하지 않거나, 검증 옵션 미지정 시 none을 사실상 허용하는 방식으로 동작해 서명 검증 우회를 유발한다. Apache Pulsar는 alg=none일 때 서명을 검증하지 않는 문제가 보고되었고, jsonwebtoken은 jwt.verify()에서 알고리즘을 명시하지 않을 때 none 기본 처리로 검증 우회가 가능했으며, python-jose는 alg=none 토큰을 서명 검증 없이 허용하는 문제가 보고되었다.

3. JWT 클레임 위변조 취약점 (CVE-2022-39227)

python-jwt 3.3.4 이전 버전은 JWS Compact 직렬화와 JWS JSON 직렬화 처리 과정의 불일치로 인해 서명 검증이 통과한 값과 애플리케이션이 실제로 사용하는 클레임이 달라질 수 있다. 공격자는 비밀 키를 모르더라도 기존 토큰의 서명을 재사용하는 방식으로 클레임을 변조해 인증을 우회할 수 있으며, 해당 취약점은 CVSS 9.1점(Critical)으로 평가되었다.

 

2) 알고리즘 혼동 공격

1. json-web-token 라이브러리 취약점 (CVE-2023-48238)

Node.js용 json-web-token 3.1.1 이전 버전은 검증에 사용할 알고리즘을 JWT 헤더의 alg 필드에서 추출하는 설계 결함이 있다. 서버가 RS256을 사용 중이고, 공격자가 공개키를 확보한 경우에 공격자는 alg를 HS256으로 설정하고 공개키를 HMAC 키처럼 사용하게 하여 개인 키 없이도 토큰을 위조할 수 있다.

2. cjwt 라이브러리 취약점 (CVE-2024-54150)

C 기반 JWT 라이브러리 cjwt의 v2.2.0은 토큰 검증 과정에서 서명 방식(HMAC vs RS/EC/PS)을 엄격히 구분하지 못해 알고리즘 혼동(algorithm confusion)이 발생할 수 있다. 이로 인해 검증 로직이 토큰의 alg와 키 타입을 안전하게 강제하지 않는 구성에서는, 공격자가 공개키를 HMAC 키처럼 취급하게 만들어 개인키 없이도 위조 토큰이 검증을 통과하도록 유도할 수 있다. 해당 취약점은 v2.3.0에서 수정됐다.

 

3) 키 검색 매개변수 주입 공격

1. kid 파라미터 경로 순회 및 SQL 인젝션

서버가 kid 값을 파일 경로나 데이터베이스 조회 쿼리에 직접 연결해 사용하는 경우, 공격자는 경로 순회 문자열이나 SQL 구문을 주입하여 임의의 키를 선택하게 만들거나 키 조회 로직을 교란할 수 있다. 결과적으로 잘못된 키로 검증이 진행되거나, 키 저장소 정보 노출로 이어질 수 있다.

 

4) 키 관리 실패 취약점

1. 하드코딩된 비밀 키 (CVE-2025-7079 / CVE-2025-6950)

서명 키가 코드나 펌웨어에 하드코딩된 경우 공격자는 해당 값을 확보해 임의의 JWT를 위조하고 인증을 우회할 수 있다. CVE-2025-7079는 bluebell-plus의 jwt.go 파일에 ‘bluebell-plus’ 문자열이 하드코딩된 사례이며, CVE-2025-6950는 Moxa 네트워크 보안 장비 및 라우터에서 JWT 서명용 하드코딩 키 사용이 보고된 사례다.

2. iss 클레임 형식 위반 (CVE-2025-30144)

fast-jwt 5.0.6 이전 RFC 7519 관점에서 문자열이어야 하는 iss 클레임에 문자열 배열을 허용하는 검증 결함이 존재한다. 공격자는 정상 발행자와 악성 발행자를 혼합한 iss 배열을 구성해 검증 로직을 우회할 수 있다.

3. 서명 키 회전 및 폐기 실패에 따른 내부자 악용

조직이 JWT 서명 키를 장기간 운영하면서 담당자 변경이나 권한 조정 시 키 회전 또는 폐기 절차를 수행하지 않으면, 유출된 키로 유효한 JWT가 지속적으로 생성될 수 있다. 서명이 정상 검증되는 한 서버와 관제는 이를 정상 트래픽으로 오인할 수 있어 탐지가 지연되고, 사고가 외부 신고나 조사 이후에 식별되는 상황이 발생할 수 있다.

 

탐지 및 방어 전략

내부 키 유출을 포함한 모든 JWT 공격 벡터에 대응하기 위해 서버 측 검증 로직을 강화하고, 실시간 침해 탐지 체계 구축이 필요하다.

 

1) 안전한 키 관리 시스템 구축 및 HS256 대체 전략

1. 하드웨어 보안 모듈 (HSM) 또는 클라우드 KMS 활용

서명 키 자료를 애플리케이션 서버의 파일 시스템이나 메모리에서 격리하는 것이 내부자 위협 대응의 핵심이다. 서명 키(비밀 키/개인 키)는 HSM(Hardware Security Module) 또는 클라우드 기반 KMS(Key Management Service)에 저장하고, 애플리케이션 서버는 키 값을 직접 취급하지 않고 서명 연산 API를 호출하는 방식으로 사용해야 한다. 이는 내부자가 서버에 접근하더라도 키 자료를 획득하는 것을 물리적으로 차단한다.

2. 비대칭키(RS256)로의 전환 및 Key Rotation

내부자 위협과 키 노출 리스크를 낮추기 위해 대칭키(HS256) 단일 공유 구조보다 개인 키·공개 키 구조의 비대칭키(RS256) 사용을 우선 고려해야 한다. 개인 키는 HSM/KMS 내부에 보관되고, 공개 키는 외부에 배포될 수 있으나 개인 키의 기밀성이 유지되는 한, 공격자가 유효한 토큰을 위조할 수 없다. 또한 침해 가능성을 전제로 키 수명과 교체 주기를 정의하고, 주기적으로 키를 회전 및 폐기하는 운영 절차를 마련해야 한다. 

 

2) 서버 측 엄격한 토큰 검증 구현 

JWT 라이브러리를 사용하더라도, 개발자는 표준 스펙에서 허용하는 모든 잠재적 위험을 명시적으로 차단해야 한다.

1. 알고리즘 화이트리스트 강제화

JWT 검증 시, 서버가 허용하는 알고리즘(예: RS256)을 라이브러리에 명시적으로 고정해야 한다. 이는 alg=none 같은 안전하지 않은 알고리즘의 수용을 차단하고, 알고리즘 혼동 공격 시도를 무력화한다.

2. Critical Claims에 대한 Strict Validation

exp (만료 시간), iss (발급자), aud (수신자) 등 표준 클레임에 대한 검증을 누락 없이 수행하도록 한다. Access Token을 수신할 때 exp 유효성 검증을 통해 만료된 토큰 사용을 차단해야 한다.

3. 키 참조 매개변수 방어

kid, jku, jwk 같은 동적 키 참조 메커니즘은 주입 공격 벡터를 제공하므로, 사용을 지양해야 한다. 불가피하게 사용해야 할 경우, kid 값에 대해 Path Traversal 패턴 및 SQL Injection 패턴을 포함한 강력한 입력 필터링(화이트리스트 기반)을 적용할 것을 권고한다.

 

3) 토큰 수명 관리 및 무효화 전략

1. Access Token의 수명 관리

Access Token은 탈취 시 피해 범위(Blast Radius)를 최소화하기 위해 수 분 이내의 짧은 수명(exp)으로 발행해야 한다. 장기적인 세션 유지는 Refresh Token으로 분리하고, Refresh Token에는 재사용 방지를 위한 회전(Rotation)과 일회성 사용 같은 정책을 적용하는 구성이 권장된다.

2. 실시간 토큰 무효화(Revocation) 시스템

키 유출이나 강제 로그아웃 발생 시, 즉각적인 토큰 무효화를 위한 중앙 집중식 Revocation List를 구축한다. 각 API 요청은 토큰의 jti 클레임을 기준으로 Revocation List를 조회해, 목록에 존재하면 요청을 차단한다. Revocation List 항목은 토큰의 만료 시점(exp)까지 TTL로 자동 만료되도록 관리한다.

 

4) 침해 탐지 및 모니터링 체계

1. 상세 로깅 및 SIEM 연동

JWT 검증 실패 이벤트(Invalid Signature, Algorithm Mismatch, Claim Errors 등)를 상세히 기록하고, 이를 중앙 집중식 SIEM 시스템에 연동한다.

2. 이상 징후 자동 탐지 시스템 구축

단기간 내에 동일한 토큰의 과도한 API 호출(Replay Attack 시도)이나, 지리적으로 비정상적인 접근 분포 또는 토큰 만료 시간(exp)이 비정상적으로 긴 토큰이 유입되는 경우 등 이상 징후를 감지하고 즉각적인 경보를 발생시켜 추가 피해를 막도록 한다.
 

결론

JWT 기반 인증 시스템의 보안 실패는 곧 기업의 핵심 자산에 대한 광범위한 침해로 이어질 수 있다. 특히 서명 키 관리 실패는 인증 체계 전반을 무력화하는 고위험 단일 실패 지점으로 격상될 수 있다. 따라서 서명 키를 HSM 또는 KMS 내부에 격리하고, 비대칭키 기반(RS256) 전환과 키 회전 정책을 최우선 과제로 설정해야 한다. 동시에 알고리즘 고정, 핵심 클레임 검증, 키 참조 입력 방어를 포함한 엄격한 검증 로직을 구현해 공격 성공 가능성을 사전에 낮춰야 한다. 마지막으로 로그 기반 탐지와 이상 징후 자동화를 결합해 침해 징후를 조기에 식별하고 피해를 최소화하는 운영 체계 구축을 권고한다.

 

출처

 

AhnLab TIP를 구독하시면 연관 IOC 및 상세 분석 정보를 추가적으로 확인하실 수 있습니다. 자세한 내용은 아래 배너를 클릭하여 확인해보세요.