1. 개요
2026년 10월 21일(현지 시각), 오라클(Oracle)은 정기 보안 패치(CPU)를 배포한 지 단 하루 만에 긴급 보안 경보(Out-of-Band Security Alert)를 통해 Java SE 및 GraalVM의 치명적인 보안 취약점인 CVE-2026-91403을 공개했습니다. 위험도 점수는 **CVSS v3.1 기준 9.1/10점(Critical)**으로 평가됐습니다.
이번 취약점이 특이한 이유는 Java의 소스 코드나 메모리 처리 버그가 아니기 때문입니다. 전 세계 주요 브라우저와 운영체제가 기본적으로 신뢰하는 인도의 글로벌 인증기관(CA) 'eMudhra'에서 발급한 중간 인증서(Intermediate CA) 8개의 개인키가 사실상 만천하에 노출된 사건에 대해, Java 런타임(JSSE)이 이 문제의 인증서들을 여전히 '신뢰함'으로 처리하고 있어 발생한 취약점입니다.
특히 전날(10월 20일) 배포된 10월 정기 보안 업데이트 버전조차 이 취약점에 그대로 노출되어 있어, 백엔드 서버나 컨테이너 환경에서 Java를 운영하는 조직의 즉각적인 추가 패치가 요구됩니다.
2. 사전 배경지식: 왜 브라우저는 안전한데 Java 백엔드 서버만 비상이 걸렸을까?
웹 생태계의 HTTPS(TLS) 암호화 통신은 **인증기관(CA)의 연쇄적인 신뢰(Chain of Trust)**를 기반으로 동작합니다. 최상위 루트 인증서(Root CA)가 중간 인증서(Intermediate CA)를 보증하고, 이 중간 인증서가 개별 웹사이트(*.google.com, *.naver.com 등)의 진짜 신원을 서명해 주는 구조입니다. 즉, 중간 인증서의 개인키를 손에 넣은 공격자는 세상에 존재하는 그 어떤 도메인 이름으로든 '정상적으로 서명된 가짜 인증서'를 마음대로 찍어낼 수 있습니다.
eMudhra의 중간 인증서 사고가 터지자 Chrome, Firefox, Safari, Windows 등 주요 브라우저와 OS는 10월 17일~20일 사이에 해당 인증서를 즉시 차단했습니다. 또한 최신 브라우저들은 인증서 투명성(CT: Certificate Transparency) 로그를 강제하기 때문에 몰래 찍어낸 가짜 인증서를 애초에 거부합니다.
하지만 Java 애플리케이션(Spring Boot 백엔드 서버 등)은 다릅니다.
독립된 인증서 저장소(
cacerts) 사용: 직접 설치한 JDK나 Docker 컨테이너 이미지 내부의 Java 런타임은 OS의 인증서 목록과 별개로 자체 내장된 신뢰 저장소(Truststore)를 참조합니다.CRL/OCSP 및 CT 미강제: Java의 기본 TLS 구현체인 **JSSE(Java Secure Socket Extension)**는 성능과 네트워크 환경 문제로 인해 기본적으로 실시간 인증서 폐기 확인(Revocation Check)이나 CT 로그 검증을 강제하지 않습니다.
결국 별도의 패치를 하지 않은 Java 서버는 이미 폐기되어 해커의 장난감이 된 중간 인증서를 끝까지 '절대적으로 신뢰하는 기관'으로 믿게 됩니다.
3. CVE-2026-91403: 취약한 중간 인증서 신뢰로 인한 TLS 우회 및 MITM
💡 비전공자를 위한 한 줄 비유
전 세계 모든 은행이 믿고 거래하는 '국제 신분증 발급소(eMudhra)'가 직인 금고 비밀번호를 어이없게도 **'0004'**로 설정해 두어 도둑들에게 직인을 털렸습니다. 일반 시중 은행(웹 브라우저)들은 긴급 공문을 받고 그 직인이 찍힌 신분증을 즉시 무효 처리했지만, 공문을 받지 않고 옛날 규정집만 보던 기업 금고지기(Java 서버)는 도둑이 위조해 온 신분증의 직인만 보고 진짜 고객으로 믿어 금고 문을 활짝 열어주는 상황입니다.
1) 핵심 원인: 개인키가 '숫자 4'로 만들어진 어처구니없는 암호학적 결함
2025년 4월 30일, 인증기관 eMudhra는 총 24개의 중간 인증서를 생성했습니다. 이 중 타원곡선 암호(ECC P-384) 기반의 중간 인증서 8개를 생성하는 과정(Key Wrapping/Unwrapping 단계)에서 치명적인 구현 오류가 발생했습니다.
원래 P-384 개인키는 약 $10^{115}$개의 경우의 수를 가진 거대한 무작위 정수여야 합니다. 그러나 문제의 인증서 8개는 **개인키의 마지막 1바이트를 제외한 앞의 모든 바이트가 0으로 채워진 상태(즉, 256 미만의 작은 정수)**로 생성됐습니다.
심지어 그중 하나의 개인키는 **단순히 숫자 4**였으며, 나머지 7개도 56, 66, 115, 155, 156, 230, 237에 불과했습니다. 1부터 255까지 숫자만 대입해 보면 공개된 인증서에서 개인키를 1밀리초(ms)도 안 되어 복구할 수 있었고, 실제로 2026년 10월 17일 보안 연구원이 8개의 개인키를 모두 추출해 증명하면서 사태가 수면 위로 드러났습니다.
2) 공격 시나리오 및 침해 과정 (MITM)
[공격자: 노출된 키(4 등)로 위조 인증서 생성] ➔ [Java 서버의 외부 API 통신 가로채기] ➔ [JSSE가 위조 인증서를 정상 승인] ➔ [기밀 데이터 탈취 및 변조]
위조 인증서 발급: 공격자는 이미 공개된 eMudhra 중간 인증서의 개인키(예:
4)를 이용해, 우리 백엔드 서버가 자주 통신하는 외부 결제사, OAuth 인증 서버, 클라우드 API 도메인 명의의 TLS 인증서를 위조해 서명합니다.네트워크 중간자 공격(MITM): 공격자가 ARP 스푸핑, BGP 하이재킹, DNS 오염 또는 프록시 장악 등을 통해 Java 애플리케이션(
HttpClient,RestTemplate,WebClient, JDBC TLS 등)이 외부 서버로 보내는 네트워크 트래픽을 가로챕니다.JSSE 검증 우회: Java의 JSSE 컴포넌트는 공격자가 제시한 위조 인증서의 서명을 검증합니다. 위조 인증서는 취약한 eMudhra 중간 인증서로 정당하게 서명되어 있고, 그 중간 인증서는 Java 기본
cacerts에 있는 eMudhra 루트 인증서(emSign ECC Root CA - C3 / G3)로 이어지므로 완벽히 유효한 인증서로 통과시킵니다.데이터 탈취 및 무결성 훼손: 암호화 통신이 공격자와 맺어지면서 API Key, 사용자 개인정보, 인증 토큰이 평문으로 탈취(Confidentiality 침해)되거나, 공격자가 조작한 가짜 응답 데이터를 Java 서버가 그대로 수신하여 처리(Integrity 침해)하게 됩니다.
4. 배포 환경별 영향도 및 헷갈리기 쉬운 함정
현재 운영 중인 Java 환경이 어떻게 설치되었는지에 따라 취약점 노출 여부와 패치 방식이 달라집니다.
구분 | 직접 설치한 JDK / 컨테이너 이미지 (최고 위험) | Linux OS 패키지 OpenJDK (조건부 위험) |
|---|---|---|
대상 예시 | Oracle JDK, Temurin, Corretto 등 직접 다운로드 및 Docker 베이스 이미지 |
|
인증서 참조 방식 | JDK 내부에 번들링된 자체 | OS의 중앙 인증서 저장소( |
조치 방법 | JDK 자체를 | OS의 |
⚠️ 실무자가 가장 많이 착각하는 2가지 함정
"엊그제(10월 20일) 정기 업데이트를 적용했으니 안전하다?" ➔ 거짓!
10월 20일에 배포된 정기 업데이트 버전(
25.0.5,21.0.13,17.0.21,11.0.33, 초기8u511)은 이 인증서 차단 목록이 반영되기 전에 빌드되어 모두 취약점 대상입니다. 반드시 10월 21일 긴급 배포된 리비전 버전으로 다시 올려야 합니다.
"내 서버는 외부에서 접속을 받기만 하니 상관없다?" ➔ 거짓!
이 취약점은 Java가 **클라이언트(Client)**로서 다른 서버의 HTTPS 인증서를 검증할 때 발생합니다. 백엔드 서버라도 외부 소셜 로그인, PG사 결제 연동, 사내 MSA 간 HTTPS 통신, 웹훅 발송 등을 수행한다면 공격 대상이 됩니다.
5. 해결 방법 및 대응 방안
1) 보안 패치 버전(.0.2 리비전)으로 즉시 업데이트 (권장)
오라클 및 OpenJDK 배포판 벤더들이 10월 21일 이후 긴급 배포한 아래 버전으로 JDK/JRE를 업데이트하고 애플리케이션(및 컨테이너)을 재빌드·재시작해야 합니다.
Java 27:
27.0.1.0.2이상Java 25:
25.0.5.0.2이상Java 21 (LTS):
21.0.13.0.2이상Java 17 (LTS):
17.0.21.0.2이상Java 11 (LTS):
11.0.33.0.2이상Java 8 (LTS): 2026년 10월 21일 이후 재배포된
8u511빌드 (기존8u511과 버전명이 같으므로 다운로드 일자 및 빌드 번호 확인 필수)
2) 당장 JDK 업데이트가 불가능할 경우의 임시 완화책 (Workaround)
서버 재기동이나 JDK 버전 업그레이드가 당장 어려운 환경이라면, OpenJDK 및 오라클(https://ops.java/emudhra)에서 제공하는 차단 목록 파일을 수동으로 덮어써서 해결할 수 있습니다.
방법 A:
blocked.certs파일 교체 (외부망 통신이 막힌 폐쇄망 서버에 권장)OpenJDK 권고 페이지에서 문제의 인증서 8개가 등록된 최신
blocked.certs파일을 다운로드합니다.JDK 11 ~ 27:
$JAVA_HOME/lib/security/blocked.certs파일을 교체합니다. - JDK 8: 파일 이름을blacklisted.certs로 변경한 뒤,$JAVA_HOME/jre/lib/security/blacklisted.certs경로에 덮어씁니다.
방법 B: Linux OS 패키지 매니저 업데이트 (OS 저장소 연동 환경)
Debian/Ubuntu(
ca-certificates업데이트 후update-ca-certificates실행), RHEL/Fedora(ca-certificates업데이트 후update-ca-trust실행) 등 OS 레벨의 신뢰 저장소를 최신화합니다.
방법 C: TLS 인증서 폐기 확인(Revocation Check) 활성화
애플리케이션 실행 시 시스템 프로퍼티로
-Dcom.sun.net.ssl.checkRevocation=true및ocsp.enable=true를 설정하여 실시간으로 폐기 여부를 검증하도록 강제합니다. (단, 외부 CRL/OCSP 서버와 통신이 가능해야 하며 약간의 핸드셰이크 지연이 발생할 수 있습니다.)
댓글
로그인 상태를 확인하고 있어요.