กลับ

2026/10/23

เมื่อวันที่ 20 ตุลาคม 2026 มีการเปิดเผยต่อสาธารณะว่า private key ของผู้ออกใบรับรองระดับกลาง (intermediate CA) แปดตัวที่ดำเนินการโดย eMudhra ซึ่งเป็น CA ของอินเดีย สามารถถูกกู้คืนได้โดยใครก็ได้ เบราว์เซอร์บล็อกใบรับรองเหล่านี้ในวันเดียวกับที่มีการรายงานปัญหา อย่างไรก็ตาม แอปพลิเคชัน Java จำนวนมากยังคงเชื่อถือใบรับรองเหล่านี้อยู่จนถึงทุกวันนี้ และจะยังคงเชื่อถือต่อไปจนกว่าจะมีผู้อัปเดต JDK หรือแก้ไข truststore ของ JDK

เมื่อวันที่ 21 ตุลาคม 2026 BellSoft ได้เผยแพร่ผลการทดสอบซึ่งแสดงให้เห็นว่า Java runtime จำนวนมากยังคงยอมรับใบรับรองเหล่านี้ บทความนี้สรุปสิ่งที่พวกเขาค้นพบ ซึ่งเราได้ตรวจยืนยันด้วยการทดสอบของเราเอง พร้อมอธิบายว่าทำไม Java จึงได้รับผลกระทบในขณะที่เบราว์เซอร์ไม่ได้รับผลกระทบ วิธีตรวจสอบระบบของคุณ และสิ่งที่ควรทำ

ประเด็นสำคัญ

  • คีย์ของ intermediate CA ของ eMudhra แปดตัวถูกเปิดเผย ใครก็ตามที่มีคีย์ตัวใดตัวหนึ่งสามารถออกใบรับรองให้กับ hostname ใดก็ได้ และไคลเอนต์ที่เชื่อถือเชนนี้จะยอมรับใบรับรองนั้น
  • เบราว์เซอร์บล็อกแล้ว แต่ Java ใช้ truststore ของตัวเอง และโดยค่าเริ่มต้นไม่ตรวจสอบทั้งการเพิกถอน (revocation) และ Certificate Transparency
  • JDK บิลด์จากผู้จำหน่ายหลายรายยังคงเชื่อถือเชนที่ได้รับผลกระทบบางส่วนหรือทั้งหมด รวมถึงใน container image
  • ระหว่างที่ผู้จำหน่ายยังไม่ออก JDK ที่แก้ไขแล้ว ให้ลบ root ที่ได้รับผลกระทบทั้งสองตัวออกจาก truststore หลังจากทดสอบแล้ว การตรวจสอบการเพิกถอนเป็นทางเลือกที่สอง

เกิดอะไรขึ้น

eMudhra สร้าง private key ของใบรับรอง intermediate CA แปดใบอย่างไม่ถูกต้อง ทำให้ใครก็สามารถกู้คืนคีย์ได้จากใบรับรองสาธารณะ ซึ่งเป็นไปได้มาตั้งแต่ intermediate เหล่านี้ถูกออกเมื่อเดือนเมษายน 2025

เมื่อวันที่ 17 ตุลาคม 2026 ผู้รายงานได้พิสูจน์ปัญหาด้วยการลงนามข้อความ “We have a problem here” ด้วยคีย์ทั้งแปดตัวทีละตัว แล้วส่งลายเซ็นไปยัง root program ของเบราว์เซอร์ต่าง ๆ รายละเอียดถูกเปิดเผยต่อสาธารณะเมื่อวันที่ 20 ตุลาคม

  • intermediate หกตัวออกใบรับรอง TLS: TLS CA แบบ DV, OV และ EV ของ eMudhra รุ่น G3B และ G3C
  • intermediate สองตัวออกใบรับรองสำหรับ code signing
  • intermediate แต่ละตัวมีอยู่สองแบบ: แบบที่ลงนามโดย root emSign ของ eMudhra และแบบที่ cross-sign โดย root AAA Certificate Services ของ Sectigo ใน truststore ของ JDK เชนเหล่านี้จะสิ้นสุดที่ root ตัวใดตัวหนึ่งจากสองตัว คือ emSign ECC Root CA - G3 (ผ่าน cross-sign) หรือ AAA Certificate Services

Firefox และ Chrome บล็อก intermediate เหล่านี้ตั้งแต่วันที่ 17 ตุลาคม Apple ตั้งแต่วันที่ 19 ตุลาคม และ Microsoft ตั้งแต่วันที่ 20 ตุลาคม eMudhra เพิกถอนทั้งแปดตัวเมื่อวันที่ 18 และ 19 ตุลาคม ด้วยเหตุผล CA_COMPROMISE ส่วนดิสทริบิวชัน Linux ต่าง ๆ ได้ออกแพ็กเกจใบรับรอง CA ที่อัปเดตแล้ว

ผลกระทบคืออะไร

ใครก็ตามที่มีคีย์ตัวใดตัวหนึ่งเหล่านี้สามารถสร้างใบรับรองสำหรับ hostname ใดก็ได้ เช่น ธนาคารของคุณ ผู้ให้บริการคลาวด์ของคุณ หรือ API gateway ของคุณเอง ไคลเอนต์ที่เชื่อถือเชนนี้จะยอมรับใบรับรองนั้น หากผู้โจมตีมีช่องทางดักจับทราฟฟิกด้วย เช่น เครือข่ายที่ถูกบุกรุกหรือระเบียน DNS ที่ถูกแก้ไข ผู้โจมตีอาจ:

  • อ่านและแก้ไขทราฟฟิกระหว่างแอปพลิเคชัน Java กับบริการที่แอปพลิเคชันเรียกใช้ รวมถึงรหัสผ่าน API key และโทเค็น
  • ปลอมตัวเป็น package repository เซิร์ฟเวอร์อัปเดต หรือ API ของพาร์ทเนอร์
  • ใช้ intermediate สำหรับ code signing ทั้งสองตัวลงนามโค้ดที่ดูเหมือนมาจากผู้เผยแพร่ที่เชื่อถือได้

intermediate เหล่านี้ไม่เคยปรากฏใน log ของ Certificate Transparency (CT) ดังนั้นเบราว์เซอร์ที่บังคับใช้ CT จึงไม่อาจถูกโจมตีผ่าน intermediate เหล่านี้ได้ ส่วนคีย์เหล่านี้เคยถูกใช้โจมตีไคลเอนต์ที่ไม่ตรวจสอบ CT หรือไม่นั้นยังไม่ทราบแน่ชัด และ Java ก็เป็นหนึ่งในไคลเอนต์กลุ่มนั้น

ทำไม Java ยังคงเชื่อถือใบรับรองเหล่านี้

Mozilla, Google, Apple และ Microsoft ส่งการบล็อกผ่านช่องทางของตนเอง เช่น OneCRL, CRLite และ CRLSets แต่ TLS implementation เริ่มต้นของ JDK ไม่ได้อ่านช่องทางใดเลย ค่าเริ่มต้นสามข้อทำให้ Java ยังคงมีความเสี่ยง:

  • ใช้ truststore ของตัวเอง หากแอปพลิเคชันไม่ได้ตั้งค่า javax.net.ssl.trustStore Java จะเชื่อถือ root ใน $JAVA_HOME/lib/security/jssecacerts หากมีไฟล์นั้นอยู่ หรือใน $JAVA_HOME/lib/security/cacerts หากไม่มี ไฟล์ cacerts ที่ผู้จำหน่ายให้มาพร้อม JDK จะไม่ได้รับ blocklist ของเบราว์เซอร์และไม่ได้รับการอัปเดต CA ของระบบปฏิบัติการ ไฟล์นี้จะเปลี่ยนก็ต่อเมื่อผู้ดูแลระบบแก้ไข หรือเมื่อการอัปเดต JDK เขียนทับไฟล์เท่านั้น
  • ไม่ตรวจสอบการเพิกถอน Java จะไม่ตรวจสอบการเพิกถอนจนกว่าคุณจะเปิดใช้งาน (com.sun.net.ssl.checkRevocation มีค่าเริ่มต้นเป็น false) ดังนั้นการกำหนดค่าเริ่มต้นจะมองไม่เห็นการเพิกถอนของ CA
  • ไม่มี Certificate Transparency JDK ไม่บังคับใช้ CT ดังนั้นการป้องกันที่คุ้มครองผู้ใช้เบราว์เซอร์จึงไม่มีผลกับ Java

หน้าเปิดเผยข้อมูลระบุ host ทดสอบสิบสองตัว หนึ่งตัวต่อการจับคู่ intermediate กับ root แต่ละแบบ BellSoft ทดสอบบน Linux เมื่อวันที่ 21 ตุลาคม และเราทดสอบซ้ำเมื่อวันที่ 23 ตุลาคม:

Runtime ที่ทดสอบhost ทดสอบที่ถูกเชื่อถือ
Liberica JDK 21.0.1012 จาก 12
Liberica JDK 8u504 (container image)12 จาก 12
Liberica JDK 21.0.12.1 (container image)12 จาก 12
Eclipse Temurin 25.0.4.16 จาก 12 (เฉพาะ root ของ eMudhra)
Eclipse Temurin 21.0.12.1 (container image, การทดสอบของเรา)6 จาก 12 (เฉพาะ root ของ eMudhra)
Arch Linux OpenJDK 27 (CA store ของระบบ)6 จาก 12 (เฉพาะ root ของ eMudhra)

ความแตกต่างอยู่ที่ root ของ Sectigo โดย Mozilla ได้นำ AAA Certificate Services ออกไปแล้ว และ truststore ของ Temurin และ Arch Linux สร้างจากรายการของ Mozilla จึงเหลือเพียงเส้นทางผ่าน eMudhra ที่ยังเปิดอยู่ ส่วน truststore ของ Liberica JDK ยังคงมี root ดังกล่าว ทั้งสองเส้นทางจึงยังเปิดอยู่

ระบบใดบ้างที่ควรตรวจสอบ

  • JDK แบบ standalone ที่แตกไฟล์จาก archive ของผู้จำหน่ายหรือติดตั้งด้วยตัวติดตั้งของผู้จำหน่าย JDK เหล่านี้ใช้ cacerts ของตัวเอง
  • Container image ที่มาพร้อม JDK ใน image ที่ BellSoft ทดสอบ การอัปเดตแพ็กเกจ ca-certificates ของระบบปฏิบัติการไม่ได้ทำให้ cacerts ของ JDK เปลี่ยนแปลง การ pull base image ที่ใหม่กว่าก็ไม่ช่วยเช่นกัน: image Liberica ของวันที่ 21 ตุลาคมที่นำมาทดสอบยังคงเชื่อถือ host ทดสอบครบทั้งสิบสองตัว
  • แอปพลิเคชันที่มี truststore ของตัวเอง ซึ่งกำหนดผ่าน javax.net.ssl.trustStore ในการกำหนดค่าของ application server หรือภายในแอปพลิเคชันเอง ให้ตรวจสอบว่ามี root ทั้งสองตัวนี้อยู่หรือไม่
  • OpenJDK ที่แพ็กเกจโดยดิสทริบิวชัน ให้ตรวจสอบว่า cacerts ชี้ไปที่ใด หากลิงก์ไปยัง CA store ของระบบ (บน Arch Linux เป็น symlink ไปยัง /etc/ca-certificates/extracted/java-cacerts.jks) การอัปเดต ca-certificates ของดิสทริบิวชันจะแก้ปัญหาได้ หน้าเปิดเผยข้อมูลระบุเวอร์ชันแพ็กเกจที่แพตช์แล้วไว้

วิธีตรวจสอบ JDK ของคุณ

ขั้นแรก ให้มองหา root ทั้งสองตัวใน truststore ชื่อ alias แตกต่างกันไปตามผู้จำหน่าย (Liberica JDK ใช้ emsigneccrootcag3 [jdk] ส่วน Temurin ใช้ cn_emsign_ecc_root_ca__g3,...) จึงควรค้นหาจาก subject ของใบรับรอง ยังมี root ของ emSign ตัวอื่นอีกหลายตัว แต่มีเพียง emSign ECC Root CA - G3 เท่านั้นที่ได้รับผลกระทบ บน JDK 9 ขึ้นไป:

keytool -list -v -cacerts -storepass changeit | grep -E '^Alias name:|^Owner:' | grep -B1 -E '^Owner: CN=(emSign ECC Root CA - G3|AAA Certificate Services),'

keytool ของ Java 8 ไม่มีตัวเลือก -cacerts และ truststore ของ Java 8 อยู่ภายใต้ jre/:

keytool -list -v -keystore "$JAVA_HOME/jre/lib/security/cacerts" -storepass changeit | grep -E '^Alias name:|^Owner:' | grep -B1 -E '^Owner: CN=(emSign ECC Root CA - G3|AAA Certificate Services),'

แต่ละรายการที่ตรงกันจะแสดงชื่อ alias ตามด้วย subject ของใบรับรอง ให้จดชื่อ alias ไว้ เพราะต้องใช้ในขั้นตอนแก้ไข

จากนั้นทดสอบการเชื่อมต่อจริง โปรแกรมเล็ก ๆ นี้เชื่อมต่อไปยัง host ทดสอบทั้งสิบสองตัว และทำงานได้บน Java 8 และทุกรุ่นหลังจากนั้น:

import java.net.URI;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLHandshakeException;
public class Check {
    public static void main(String[] args) {
        for (String level : new String[]{"dv", "ov", "ev"})
            for (String gen : new String[]{"g3b", "g3c"})
                for (String via : new String[]{"", "-aaa"}) {
                    String host = level + "-" + gen + via + "-emudhra.we-have-a-problem.com";
                    try {
                        HttpsURLConnection c = (HttpsURLConnection) URI.create("https://" + host).toURL().openConnection();
                        c.setConnectTimeout(10000);
                        c.setReadTimeout(10000);
                        c.connect();
                        c.disconnect();
                        System.out.println("TRUSTED       " + host);
                    } catch (SSLHandshakeException e) {
                        System.out.println("TLS_REJECTED  " + host);
                    } catch (Exception e) {
                        System.out.println("ERROR         " + host + " (" + e + ")");
                    }
                }
    }
}

บน JDK 11 ขึ้นไป ให้รันด้วย java Check.java บน Java 8 หรือเครื่องที่ติดตั้งเพียง JRE ให้คอมไพล์ก่อนด้วย javac Check.java (บนเครื่องที่มี JDK หากจำเป็น) แล้วรัน java Check บรรทัด TRUSTED หมายความว่า JDK นี้ยอมรับ intermediate ที่ถูกบุกรุก ส่วน ERROR หมายความว่าเชื่อมต่อไปยัง host ไม่ได้ ซึ่งไม่ได้พิสูจน์อะไรเลยไม่ว่าทางใด ให้รันการตรวจสอบด้วย JDK และการตั้งค่า truststore เดียวกับแอปพลิเคชันของคุณ รวมถึงภายใน container ด้วย

วิธีแก้ไข

1. ลบ root ทั้งสองตัว (แนะนำจนกว่าจะมี JDK ที่แก้ไขแล้ว)

root ทั้งสองตัวนี้คือตัวเดียวกับที่หน้าเปิดเผยข้อมูลแนะนำให้ผู้ใช้ Linux และ macOS ยกเลิกความเชื่อถือ ให้รันคำสั่งในฐานะผู้ใช้ที่เป็นเจ้าของ JDK โดยใช้ชื่อ alias ที่พบจากการตรวจสอบ ตัวอย่างเช่น บน Liberica JDK:

keytool -delete -cacerts -storepass changeit -alias "comodoaaaca [jdk]"
keytool -delete -cacerts -storepass changeit -alias "emsigneccrootcag3 [jdk]"

บน Java 8 ให้แทนที่ -cacerts ด้วย -keystore "$JAVA_HOME/jre/lib/security/cacerts" หาก truststore ของคุณมี root เพียงตัวเดียวจากสองตัว เช่นของ Temurin ให้ลบเฉพาะตัวนั้น

หากคุณไม่สามารถแก้ไขการติดตั้ง JDK ได้ ให้แก้ไขสำเนาแล้วกำหนดให้ JVM ใช้สำเนานั้น:

cp "$JAVA_HOME/lib/security/cacerts" ./trimmed-cacerts
keytool -delete -keystore ./trimmed-cacerts -storepass changeit -alias "comodoaaaca [jdk]"
keytool -delete -keystore ./trimmed-cacerts -storepass changeit -alias "emsigneccrootcag3 [jdk]"
java -Djavax.net.ssl.trustStore=./trimmed-cacerts -Djavax.net.ssl.trustStorePassword=changeit -jar app.jar

ในการทดสอบของเรา ทั้งสองวิธีทำให้ Liberica JDK 8 และ 21 ปฏิเสธ host ทดสอบทั้งสิบสองตัว ในขณะที่เว็บไซต์ทั่วไปอย่าง sectigo.com และ github.com ยังเชื่อมต่อได้ตามปกติ

สำหรับ container image ให้ลบ root ตอน build เพื่อให้ image ใหม่ทุกตัวได้รับการแก้ไข:

FROM bellsoft/liberica-runtime-container:jdk-21-musl
RUN keytool -delete -cacerts -storepass changeit -alias "comodoaaaca [jdk]" \
 && keytool -delete -cacerts -storepass changeit -alias "emsigneccrootcag3 [jdk]"

image แบบ distroless และ non-root ต้องเปลี่ยนสองอย่าง: image เหล่านี้ไม่มี shell จึงต้องใช้ RUN แบบ exec form โดยใช้หนึ่งคำสั่งต่อหนึ่งบรรทัด และรันในฐานะผู้ใช้ที่ไม่มีสิทธิ์พิเศษ จึงต้องสลับเป็น USER root ระหว่างแก้ไข แล้วสลับกลับเป็นผู้ใช้ปกติของ image หลังจากนั้น

เคล็ดลับ: การลบ root มีผลกว้างกว่าการบล็อก intermediate แปดตัว เซิร์ฟเวอร์ที่ถูกต้องชอบธรรมซึ่งมีเชนสิ้นสุดที่ root ตัวใดตัวหนึ่งเหล่านี้เท่านั้นจะใช้งานไม่ได้ ให้ทดสอบบริการที่แอปพลิเคชันของคุณเรียกใช้ก่อนนำไปใช้จริง และรันการตรวจสอบอีกครั้งหลังการอัปเดต JDK ทุกครั้ง เนื่องจากการอัปเดตหรือการติดตั้งใหม่อาจคืนค่าไฟล์ cacerts ต้นฉบับกลับมา

2. เปิดการตรวจสอบการเพิกถอน (ไม่บังคับ)

eMudhra ได้เพิกถอน intermediate ทั้งแปดตัวแล้ว ดังนั้น JVM ที่ตรวจสอบการเพิกถอนจะปฏิเสธ intermediate เหล่านี้โดยไม่ต้องแก้ไข truststore เลย ให้เปิดใช้ CRL distribution point และ OCSP:

echo 'ocsp.enable=true' > revocation.security
java -Dcom.sun.net.ssl.checkRevocation=true \
     -Dcom.sun.security.enableCRLDP=true \
     -Djava.security.properties=revocation.security \
     -jar app.jar

ในการทดสอบของเรา วิธีนี้ปฏิเสธ host ทดสอบทั้งสิบสองตัวบน Liberica JDK 8 และ 21 และบน Temurin 21 อย่างไรก็ตาม วิธีนี้เปลี่ยนวิธีตรวจสอบความถูกต้องของใบรับรองทุกใบ JVM จะค้นหาข้อมูล OCSP หรือ CRL ระหว่างการตรวจสอบเส้นทางใบรับรอง (path validation) และ handshake อาจล้มเหลวเมื่อไม่สามารถระบุสถานะการเพิกถอนได้ ก่อนเปิดใช้งานทั่วทั้งระบบ ให้ทดสอบว่าเซิร์ฟเวอร์ของคุณเข้าถึง responder ได้หรือไม่ เพิ่ม latency มากเพียงใด และระบบทำงานอย่างไรเมื่อเกิดความล้มเหลว ใช้วิธีนี้หากคุณเปิดการตรวจสอบการเพิกถอนอยู่แล้ว หรือใช้ควบคู่กับการลบ root

3. อัปเดต JDK เมื่อมีบิลด์ที่แก้ไขแล้ว

BellSoft แจ้งว่ากำลังเตรียมอัปเดต Liberica JDK ที่นำ root เหล่านี้ออก ให้ติดตามประกาศด้านความปลอดภัยของผู้จำหน่าย JDK ที่คุณใช้ เมื่อมีการอัปเดตออกมา ให้อ่าน release notes เพื่อยืนยันว่าครอบคลุมใบรับรองของ eMudhra แล้วติดตั้ง build container image ใหม่ และรันการตรวจสอบอีกครั้ง หมายเลขเวอร์ชันที่ใหม่กว่าเพียงอย่างเดียวไม่ใช่หลักฐาน

หลังแก้ไข: ทบทวนความเสี่ยงที่อาจเกิดขึ้นแล้ว

คีย์เหล่านี้สามารถถูกกู้คืนได้มาประมาณสิบแปดเดือน การแก้ไขจึงเป็นเพียงการปิดประตู แต่ไม่ได้บอกว่ามีใครเคยใช้ประตูนั้นหรือไม่ สำหรับระบบที่ส่งข้อมูลรับรองตัวตน (credentials) หรือข้อมูลที่ละเอียดอ่อน ให้ค้นหาใบรับรองที่ออกโดย intermediate ทั้งแปดตัวของ eMudhra ใน log ของ TLS inspection, proxy และ endpoint หากพบสิ่งที่น่าสงสัย ให้เปลี่ยน (rotate) credentials ที่ได้รับผลกระทบ

ในระยะยาว ให้จัดการ truststore เหมือนกับ dependency: รู้ว่าแต่ละตัวอยู่ที่ใด ใครเป็นผู้อัปเดต และการเปลี่ยนแปลงไปถึง production ได้เร็วเพียงใด เหตุการณ์นี้แสดงให้เห็นว่าการอัปเดตเบราว์เซอร์ไม่เพียงพอ เมื่อเซิร์ฟเวอร์ของคุณใช้สำเนารายการความเชื่อถือ (trust list) ของตัวเอง

ต้องการความช่วยเหลือ? LOGIQ ให้บริการซัพพอร์ตสำหรับ OpenJDK และ Linux รวมถึงการแพตช์ด้านความปลอดภัย หากคุณต้องการความช่วยเหลือในการค้นหา JDK และ truststore ทั้งหมดในระบบของคุณ หรือการนำการแก้ไขไปใช้อย่างปลอดภัย โปรดติดต่อเรา

อ่านเพิ่มเติม