Quay lại
2026/10/23
Ngày 20 tháng 10 năm 2026, thông tin được công bố rằng bất kỳ ai cũng có thể khôi phục khóa riêng (private key) của tám tổ chức cấp chứng chỉ (CA) trung gian do eMudhra, một CA của Ấn Độ, vận hành. Các trình duyệt đã chặn những chứng chỉ này ngay trong ngày vấn đề được báo cáo. Tuy nhiên, đến nay nhiều ứng dụng Java vẫn tin cậy chúng, và sẽ tiếp tục tin cậy cho đến khi có người cập nhật JDK hoặc thay đổi truststore của JDK.
Ngày 21 tháng 10 năm 2026, BellSoft đã công bố kết quả kiểm thử cho thấy nhiều Java runtime vẫn chấp nhận các chứng chỉ này. Bài viết này tóm tắt những phát hiện của họ (chúng tôi đã xác minh bằng các thử nghiệm của riêng mình), đồng thời giải thích vì sao Java bị ảnh hưởng trong khi trình duyệt thì không, cách kiểm tra hệ thống của bạn và cách xử lý.
Những điểm chính
- Tám khóa CA trung gian của eMudhra đã bị lộ. Bất kỳ ai nắm giữ một trong các khóa này đều có thể cấp chứng chỉ cho bất kỳ hostname nào, và client nào tin cậy chuỗi chứng chỉ đó sẽ chấp nhận nó.
- Các trình duyệt đã chặn chúng, nhưng Java dùng truststore riêng và theo mặc định không kiểm tra thu hồi (revocation) lẫn Certificate Transparency.
- Các bản build JDK của nhiều nhà cung cấp vẫn tin cậy một phần hoặc toàn bộ các chuỗi bị ảnh hưởng, kể cả trong container image.
- Cho đến khi nhà cung cấp phát hành JDK đã sửa lỗi, hãy xóa hai root bị ảnh hưởng khỏi truststore, sau khi đã kiểm thử. Kiểm tra thu hồi là phương án thứ hai.
Chuyện gì đã xảy ra
eMudhra đã tạo sai khóa riêng của tám chứng chỉ CA trung gian, nên bất kỳ ai cũng có thể khôi phục các khóa này từ chứng chỉ công khai. Điều này đã khả thi ngay từ khi các CA trung gian được cấp vào tháng 4 năm 2025.
Ngày 17 tháng 10 năm 2026, một người báo cáo đã chứng minh vấn đề bằng cách dùng từng khóa trong số tám khóa để ký thông điệp “We have a problem here” và gửi các chữ ký đến các chương trình root của trình duyệt. Chi tiết được công bố vào ngày 20 tháng 10.
- Sáu CA trung gian cấp chứng chỉ TLS: các TLS CA loại DV, OV và EV của eMudhra, thế hệ G3B và G3C.
- Hai CA trung gian cấp chứng chỉ ký mã (code signing).
- Mỗi CA trung gian tồn tại hai bản: một bản được ký bởi root emSign của eMudhra, một bản được ký chéo (cross-sign) bởi root
AAA Certificate Servicescủa Sectigo. Trong truststore của JDK, các chuỗi kết thúc tại một trong hai root:emSign ECC Root CA - G3(thông qua chứng chỉ ký chéo) hoặcAAA Certificate Services.
Firefox và Chrome đã chặn các CA trung gian này từ ngày 17 tháng 10, Apple từ ngày 19 tháng 10 và Microsoft từ ngày 20 tháng 10. eMudhra đã thu hồi cả tám chứng chỉ vào ngày 18 và 19 tháng 10 với lý do CA_COMPROMISE. Các bản phân phối Linux đã phát hành gói chứng chỉ CA cập nhật.
Mức độ ảnh hưởng
Bất kỳ ai nắm giữ một trong các khóa này đều có thể tạo chứng chỉ cho bất kỳ hostname nào, chẳng hạn ngân hàng của bạn, nhà cung cấp cloud của bạn hay chính API gateway của bạn. Client nào tin cậy chuỗi chứng chỉ sẽ chấp nhận chứng chỉ đó. Nếu kết hợp với một cách chặn bắt lưu lượng, ví dụ mạng bị xâm nhập hoặc bản ghi DNS bị thay đổi, kẻ tấn công có thể:
- đọc và sửa đổi lưu lượng giữa ứng dụng Java và các dịch vụ mà ứng dụng gọi tới, bao gồm mật khẩu, API key và token;
- mạo danh kho gói phần mềm (package repository), máy chủ cập nhật hoặc API của đối tác;
- thông qua hai CA trung gian ký mã, ký những đoạn mã trông như đến từ một nhà phát hành đáng tin cậy.
Các CA trung gian này chưa bao giờ xuất hiện trong nhật ký Certificate Transparency (CT), nên các trình duyệt bắt buộc CT không thể bị tấn công thông qua chúng. Hiện chưa rõ các khóa này đã từng bị dùng để tấn công những client bỏ qua CT hay chưa. Java là một trong những client như vậy.
Vì sao Java vẫn tin cậy các chứng chỉ này
Mozilla, Google, Apple và Microsoft đã triển khai lệnh chặn qua các kênh riêng của họ, như OneCRL, CRLite và CRLSets. Bản triển khai TLS mặc định của JDK không đọc kênh nào trong số đó. Ba thiết lập mặc định khiến Java vẫn bị phơi nhiễm:
- Truststore riêng. Trừ khi ứng dụng thiết lập
javax.net.ssl.trustStore, Java tin cậy các root trong$JAVA_HOME/lib/security/jssecacertsnếu tệp này tồn tại, nếu không thì trong$JAVA_HOME/lib/security/cacerts. Tệpcacertsdo nhà cung cấp đóng gói sẵn không nhận danh sách chặn của trình duyệt, cũng không nhận bản cập nhật CA của hệ điều hành. Tệp này chỉ thay đổi khi quản trị viên chỉnh sửa hoặc khi một bản cập nhật JDK thay thế nó. - Không kiểm tra thu hồi. Java không kiểm tra thu hồi trừ khi bạn bật tính năng này (
com.sun.net.ssl.checkRevocationmặc định là false), nên với cấu hình mặc định, việc CA đã thu hồi chứng chỉ hoàn toàn không được nhận biết. - Không có Certificate Transparency. JDK không bắt buộc CT, nên lớp bảo vệ đã giúp người dùng trình duyệt an toàn không áp dụng ở đây.
Trang công bố liệt kê mười hai host kiểm thử, mỗi host ứng với một tổ hợp CA trung gian và root. BellSoft đã kiểm thử chúng trên Linux vào ngày 21 tháng 10, và chúng tôi lặp lại thử nghiệm vào ngày 23 tháng 10:
| Runtime được kiểm thử | Số host kiểm thử được tin cậy |
|---|---|
| Liberica JDK 21.0.10 | 12/12 |
| Liberica JDK 8u504 (container image) | 12/12 |
| Liberica JDK 21.0.12.1 (container image) | 12/12 |
| Eclipse Temurin 25.0.4.1 | 6/12 (chỉ root eMudhra) |
| Eclipse Temurin 21.0.12.1 (container image, thử nghiệm của chúng tôi) | 6/12 (chỉ root eMudhra) |
| Arch Linux OpenJDK 27 (kho CA của hệ thống) | 6/12 (chỉ root eMudhra) |
Điểm khác biệt nằm ở root của Sectigo. Mozilla đã gỡ bỏ AAA Certificate Services từ trước, và truststore của Temurin và Arch Linux được xây dựng từ danh sách của Mozilla, nên chỉ còn đường đi qua eMudhra là mở. Truststore của Liberica JDK vẫn chứa root đó, nên cả hai đường đều mở.
Cần kiểm tra những hệ thống nào
- JDK độc lập được giải nén từ tệp lưu trữ của nhà cung cấp hoặc cài bằng trình cài đặt của nhà cung cấp. Các JDK này dùng
cacertsriêng. - Container image có kèm JDK. Trong các image mà BellSoft kiểm thử, việc cập nhật gói
ca-certificatescủa hệ điều hành không làm thay đổicacertscủa JDK. Kéo (pull) base image mới hơn cũng không giúp được gì: image Liberica ngày 21 tháng 10 được kiểm thử vẫn tin cậy cả mười hai host kiểm thử. - Ứng dụng có truststore riêng, được thiết lập qua
javax.net.ssl.trustStore, trong cấu hình máy chủ ứng dụng hoặc bên trong chính ứng dụng. Hãy kiểm tra xem các truststore này có chứa hai root trên hay không. - OpenJDK do bản phân phối đóng gói. Hãy kiểm tra
cacertstrỏ tới đâu. Nếu nó liên kết tới kho CA của hệ thống (trên Arch Linux, đó là symlink tới/etc/ca-certificates/extracted/java-cacerts.jks), bản cập nhậtca-certificatescủa bản phân phối sẽ khắc phục vấn đề. Trang công bố liệt kê các phiên bản gói đã được vá.
Cách kiểm tra JDK của bạn
Trước tiên, hãy tìm hai root trong truststore. Tên alias khác nhau giữa các nhà cung cấp (Liberica JDK dùng emsigneccrootcag3 [jdk], Temurin dùng cn_emsign_ecc_root_ca__g3,...), vì vậy hãy tìm theo subject của chứng chỉ. Còn có một số root emSign khác; chỉ emSign ECC Root CA - G3 bị ảnh hưởng. Trên JDK 9 trở lên:
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 của Java 8 không có tùy chọn -cacerts, và truststore của nó nằm trong 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),'
Mỗi kết quả khớp sẽ in ra tên alias, theo sau là subject của chứng chỉ. Hãy ghi lại các tên alias: bạn sẽ cần chúng để khắc phục.
Sau đó, hãy kiểm thử kết nối thực tế. Chương trình nhỏ dưới đây kết nối tới cả mười hai host kiểm thử và chạy được trên Java 8 cũng như mọi phiên bản sau đó:
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 + ")");
}
}
}
}
Trên JDK 11 trở lên, hãy chạy bằng java Check.java. Trên Java 8, hoặc khi chỉ cài JRE, hãy biên dịch trước bằng javac Check.java (trên một máy có JDK nếu cần) rồi chạy java Check. Dòng TRUSTED nghĩa là JDK này chấp nhận một CA trung gian đã bị xâm phạm. ERROR nghĩa là không kết nối được tới host, và điều đó không chứng minh được gì. Hãy chạy kiểm tra với cùng JDK và thiết lập truststore như ứng dụng của bạn, kể cả bên trong container.
Cách khắc phục
1. Xóa hai root (khuyến nghị cho đến khi có JDK đã sửa lỗi)
Đây cũng chính là hai root mà trang công bố khuyên người dùng Linux và macOS ngừng tin cậy. Hãy chạy các lệnh dưới quyền người dùng sở hữu JDK, với tên alias bạn đã tìm thấy khi kiểm tra. Ví dụ, trên Liberica JDK:
keytool -delete -cacerts -storepass changeit -alias "comodoaaaca [jdk]"
keytool -delete -cacerts -storepass changeit -alias "emsigneccrootcag3 [jdk]"
Trên Java 8, hãy thay -cacerts bằng -keystore "$JAVA_HOME/jre/lib/security/cacerts". Nếu truststore của bạn chỉ chứa một trong hai root, như truststore của Temurin, thì chỉ xóa root đó.
Nếu không thể sửa bản cài đặt JDK, hãy chỉnh sửa một bản sao và trỏ JVM tới bản sao đó:
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
Trong các thử nghiệm của chúng tôi, cả hai cách đều khiến Liberica JDK 8 và 21 từ chối toàn bộ mười hai host kiểm thử, trong khi các trang thông thường như sectigo.com và github.com vẫn kết nối được.
Với container image, hãy xóa các root ngay khi build để mọi image mới đều đã được khắc phục:
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 và non-root cần hai thay đổi: chúng không có shell, nên hãy dùng dạng exec của RUN với mỗi dòng một lệnh; và chúng chạy dưới một người dùng không có đặc quyền, nên hãy chuyển sang USER root khi chỉnh sửa, sau đó chuyển lại người dùng thông thường của image.
Mẹo: Xóa một root có phạm vi ảnh hưởng rộng hơn so với chặn tám CA trung gian. Một máy chủ hợp lệ có chuỗi chứng chỉ chỉ kết thúc tại một trong hai root này sẽ ngừng hoạt động. Hãy kiểm thử các dịch vụ mà ứng dụng của bạn gọi tới trước khi triển khai rộng rãi, và chạy lại bước kiểm tra sau mỗi lần cập nhật JDK, vì một bản cập nhật hoặc việc cài đặt lại có thể khôi phục tệp cacerts ban đầu.
2. Bật kiểm tra thu hồi (tùy chọn)
eMudhra đã thu hồi cả tám CA trung gian, nên một JVM có kiểm tra thu hồi sẽ từ chối chúng mà không cần thay đổi truststore. Hãy bật CRL distribution point và 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
Trong các thử nghiệm của chúng tôi, cách này đã từ chối toàn bộ mười hai host kiểm thử trên Liberica JDK 8 và 21 và trên Temurin 21. Tuy nhiên, nó thay đổi cách xác thực mọi chứng chỉ. JVM sẽ tra cứu OCSP hoặc CRL trong quá trình xác thực đường dẫn chứng chỉ (path validation), và handshake có thể thất bại khi không xác định được trạng thái thu hồi. Trước khi bật trên toàn hệ thống, hãy kiểm thử xem máy chủ của bạn có truy cập được các responder hay không, chúng làm tăng độ trễ bao nhiêu và hệ thống phản ứng thế nào khi xảy ra lỗi. Hãy dùng cách này nếu bạn vốn đã chạy với kiểm tra thu hồi, hoặc dùng song song với việc xóa root.
3. Cập nhật JDK khi có bản build đã sửa lỗi
BellSoft cho biết đang chuẩn bị các bản cập nhật Liberica JDK gỡ bỏ các root này. Hãy theo dõi thông báo bảo mật từ nhà cung cấp JDK mà bạn dùng. Khi bản cập nhật được phát hành, hãy đọc release notes để xác nhận rằng bản cập nhật có xử lý các chứng chỉ eMudhra, sau đó cài đặt, build lại container image và chạy lại bước kiểm tra. Chỉ riêng số phiên bản mới hơn thì chưa phải là bằng chứng.
Sau khi khắc phục: đánh giá mức độ phơi nhiễm
Các khóa này đã có thể bị khôi phục trong khoảng mười tám tháng, nên việc khắc phục chỉ đóng cánh cửa lại chứ không cho bạn biết đã có ai lợi dụng nó hay chưa. Với các hệ thống gửi thông tin xác thực hoặc dữ liệu nhạy cảm, hãy tìm trong nhật ký TLS inspection, proxy và endpoint các chứng chỉ do tám CA trung gian của eMudhra cấp. Nếu phát hiện điều gì đáng ngờ, hãy thay mới (rotate) các thông tin xác thực bị ảnh hưởng.
Về lâu dài, hãy quản lý truststore như quản lý dependency: biết từng truststore nằm ở đâu, ai cập nhật nó và một thay đổi mất bao lâu để đến được môi trường production. Sự cố này cho thấy cập nhật trình duyệt là chưa đủ khi máy chủ của bạn dùng bản sao danh sách tin cậy của riêng mình.
Cần hỗ trợ? LOGIQ cung cấp dịch vụ hỗ trợ cho OpenJDK và Linux, bao gồm cả vá lỗi bảo mật. Nếu bạn cần hỗ trợ để tìm mọi JDK và truststore trong môi trường của mình hoặc triển khai bản sửa lỗi một cách an toàn, hãy liên hệ với chúng tôi.
Đọc thêm
- eMudhra Intermediate CA Compromise: trang công bố, kèm các host kiểm thử và phiên bản gói đã được vá
- BellSoft: Distrusting compromised eMudhra intermediates in Java
Global
Japan
Korea
China
Taiwan
Thailand
Indonesia
Portugal
Spain
France
Germany
Egypt
Russia