01. 취약점 개요
•
'26년 2월 28일, 자바(Java) 기반 JWT* 인증 라이브러리 pac4j-jwt에서 비서명 토큰(PlainJWT)**을 통한 서명 검증 로직 우회 취약점(CVE-2026-29000) 발견
* JWT(JSON Web Token) : JSON 형식의 사용자 인증 정보를 서명 및 암호화하여 토큰으로 전달하는 표준 방식(RFC 7519)
** PlainJWT : 토큰의 진위 여부를 검증하기 위한 서명이 존재하지 않는 토큰으로 진정성(Authenticity) 및 무결성(Integrity) 보장 불가
•
비서명 토큰(PlainJWT)을 서버의 공개키로 암호화하여 JWE* 토큰으로 전송할 때 toSignedJWT 메서드가 반환하는 null 값에 대한 예외 처리가 미흡하여 발생(CVSS 10.0 만점, Ver 3.x)
◦
암호화(JWE)와 서명 검증(JWS**) 모두 구성된 환경에서 발생하며 서명 검증만 사용하는 경우 우회 불가
◦
공격자는 Claim***이 위조된 비서명 토큰(PlainJWT)을 통해 관리자 권한까지 탈취 가능
* JWE(JSON Web Encryption) : 토큰이 서버의 공개키로 암호화되어 토큰의 기밀성(Confidentiality) 보장
** JWS(JSON Web Signature) : 토큰의 진위 여부를 검증할 수 있는 서명을 통해 토큰의 진정성(Authenticity) 및 무결성(Integrity) 보장
*** Claim : sub(사용자 ID), role(권한), jti(토큰 ID) 등을 포함한 정보로 공격자가 위변조할 수 있기 때문에 서명 검증 필요
02. 영향받는 버전 및 대응방안
•
암호화(JWE) 및 서명(JWS)을 모두 사용하는 4.5.9, 5.7.9, 6.3.3 버전 미만의 pac4j-jwt 인증 라이브러리가 영향을 받으며, 해당 라이브러리를 사용하는 프레임워크(Spring Boot 등)도 버전 확인 필요
◦
Maven 버전 확인 방법 : mvn -q dependency:tree -Dincludes=org.pac4j:pac4j-jwt
◦
Gradle 버전 확인 방법 : ./gradlew -q dependencies --configuration runtimeClasspath | grep –i "pac4j-jw“
◦
영향받는 버전 사용 시 최신 보안패치 진행
제품명 | 영향받는 버전 | 보안패치 버전 |
pac4j-jwt (org.pac4j:pac4j-jwt) | 4.5.9 미만 | 4.5.9 |
5.7.9 미만 | 5.7.9 | |
6.3.3 미만 | 6.3.3 |
03. CVE-2026-29000 취약점 상세 분석
3.1. 취약점 분석
•
암호화(JWE) 및 서명 검증(JWS)이 모두 구성된 취약한 버전의 pac4j-jwt 환경에서 복호화된 JWE 토큰의 페이로드가 비서명 토큰(PlainJWT)일 때 인증 우회 취약점 발생
◦
비서명 토큰(PlainJWT)을 서버 공개키로 암호화한 JWE 토큰이 JwtAuthenticator.java 코드에 입력될 때 toSignedJWT 메서드가 반환하는 null 값에 대한 예외 처리가 미흡하여 서명 검증 로직 자체가 우회
단계 | 정상 흐름 | 공격 흐름 |
① JWE 수신 | JWS를 암호화한 JWE 수신 | PlainJWT를 암호화한 JWE 수신 |
↓ | ↓ | ↓ |
② JWE 복호화 | JWE 복호화 | JWE 복호화 |
↓ | ↓ | ↓ |
③ JWS 추출 | toSignedJWT 메서드 JWS 반환
signedJWT = JWS 할당 | toSignedJWT 메서드 null 반환
signedJWT = null 할당 |
↓ | ↓ | ↓ |
④ 서명 검증 | if (signedJWT != null)가 True로
서명 검증 진행 | if (signedJWT != null)가 False로
서명 검증 미진행 |
↓ | ↓ | ↓ |
⑤ 프로필 생성 | 검증된 Claim으로 프로필 생성 | 검증되지 않은 Claim으로 프로필 생성 |
•
취약한 버전(6.3.2)의 JwtAuthenticator.java 코드 분석을 통해 서명 검증 로직 확인
※ pac4j-jwt 6.3.2 버전의 GitHub 사이트 : https://github.com/pac4j/pac4j/tree/pac4j-parent-6.3.2/pac4j-jwt
◦
JwtAuthenticator.java 파일 197번 라인 : 암호화되어 있는 JWE를 복호화
◦
JwtAuthenticator.java 파일 198번 라인 : toSignedJWT 메서드는 Nimbus JOSE+JWT* 라이브러리 메서드로 서명 토큰(JWS, SignedJWT)이 입력되면 JWS 객체를 반환하고 비서명 토큰(PlainJWT)이 입력되면 null 값을 반환
* Nimbus JOSE+JWT : JOSE(JavaScript Object Signing and Encryption) 표준을 Java로 구현한 라이브러리
◦
JwtAuthenticator.java 파일 215번 라인 : signedJWT 변수에 null 값이 할당되면 서명 검증 로직 전체가 무시되어 토큰의 위변조 여부를 검증하지 않고 무조건 신뢰
◦
JwtAuthenticator.java 파일 244번 라인 : PlainJWT 토큰에 포함된 Claim 정보를 인증된 것으로 간주하고 createJwtProfile 함수를 호출하여 Claim에서 요구한 관리자 권한 등을 공격자에게 부여
취약한 버전의 JwtAuthenticator.java 파일 중 일부
•
패치된 버전(6.3.3)의 JwtAuthenticator.java 코드에서는 서명 검증 로직에서 예외 처리
※ pac4j-jwt 6.3.3 버전의 GitHub 사이트 : https://github.com/pac4j/pac4j/tree/pac4j-parent-6.3.3/pac4j-jwt
◦
signedJWT 변수에 null 값 할당 시 CredentialsException을 호출하여 예외 처리
패치된 버전의 JwtAuthenticator.java 파일 중 일부
3.2. 취약점 PoC
•
PoC는 Ubuntu(22.04.5), Java(17.0.18), Maven(3.6.3), pac4j-jwt(6.0.3 및 6.3.3) 환경에서 진행하며 별도의 웹 서버를 구축하지 않고 자바 코드를 프로세스로 직접 실행
※ PoC 코드 사이트 : https://github.com/kernelzeroday/CVE-2026-29000/tree/main/poc
•
공격자는 비서명 토큰(PlainJWT)를 JWE로 암호화하기 위해 서버 공개키가 필요하며 일반적으로 JWKS* 앤드포인트(/.well-known/jwks.json), TLS 인증서, 앱 설정 파일, 공개 코드 저장소 등에서 쉽게 수집할 수 있기 때문에 공격자가 공개키를 이미 확보했다고 가정하고 Poc.java 파일에서 공개키를 직접 생성하여 진행
* JWKS(JSON Web Key Set) : 공개키를 JSON 형식으로 표현한 표준(RFC 7517)으로 프레임워크에서 주로 사용
서버 공개키 확인 예시(/.well-known/jwks.json)
•
Maven 파일(pom.xml)에서 pac4j-jwt 인증 라이브러리를 취약한 버전(6.0.3)으로 설정 후 Poc.java 실행
◦
pom.xml 파일 19번 라인 : pom.xml에서 취약한 버전(6.0.3)으로 설정된 pac4j-jwt 확인
취약한 버전을 사용하는 pom.xml 파일
◦
Poc.java 파일 28~34번 라인 : 인가받지 않은 관리자 권한을 변수로 선언 및 할당
◦
Poc.java 파일 72, 73번 라인 : 인가받지 않은 관리자 권한을 Claim으로 할당
◦
Poc.java 파일 77번 라인 : 서명이 존재하지 않는 비서명 토큰(PlainJWT)을 생성
◦
Poc.java 파일 84번 라인 : 비서명 토큰(PlainJWT)을 수집한 서버 공개키로 암호화하여 JWE 생성
Poc.java 파일 중 일부
◦
취약한 버전(6.0.3)으로 설정된 pac4j-jwt 라이브러리 환경에서는 관리자 권한 탈취 가능
관리자 권한 탈취
◦
pac4j-jwt 버전을 6.3.3으로 적용하면 "A non-signed JWT cannot be accepted ..." 경고 문구와 함께 비서명 토큰(PlainJWT)을 암호화한 JWE는 거부
패치된 버전을 사용하는 pom.xml 파일
관리자 권한 탈취 불가
04. 취약점 타임라인
일자 | 주요내용 | 비고 |
2026-02-28 | • CodeAnt AI 코드 검토기가 취약점 발견, PoC 검증
• Jérôme Leleu(pac4j 관리자)에게 비공개 정보 전송 | https://www.codeant.ai/security-research/pac4j-jwt-authentication-bypass-public-key |
2026-03-02 | • 4.x, 5.x, 6.x 버전용 패치 배포 | https://github.com/pac4j/pac4j/tree/pac4j-4.5.9
https://github.com/pac4j/pac4j/tree/pac4j-parent-5.7.9
https://github.com/pac4j/pac4j/tree/pac4j-parent-6.3.3 |
2026-03-02 | • pac4j에서 보안 권고문 발표 | https://www.pac4j.org/blog/security-advisory-pac4j-jwt-jwtauthenticator.html |
2026-03-03 | • VulnCheck에서 CVE-2026-29000 지정 | https://www.vulncheck.com/advisories/pac4j-jwt-jwtauthenticator-authentication-bypass |
05. 참고자료
1.
CVE-2026-29000: Critical Authentication Bypass in pac4j-jwt - Using Only a Public Key (CVSS 10) : https://www.codeant.ai/security-research/pac4j-jwt-authentication-bypass-public-key
2.
CVE-2026-29000: pac4j-jwt JwtAuthenticator authentication bypass : https://github.com/kernelzeroday/CVE-2026-29000/tree/main/poc
3.
Security advisory for pac4j-jwt (JwtAuthenticator) : https://www.pac4j.org/blog/security-advisory-pac4j-jwt-jwtauthenticator.html




