Race Condition allows Bypass of Trust Restrictions
Description
In Eclipse Jersey versions 2.45, 3.0.16, 3.1.9 a race condition can cause ignoring of critical SSL configurations - such as mutual authentication, custom key/trust stores, and other security settings. This issue may result in SSLHandshakeException under normal circumstances, but under certain conditions, it could lead to unauthorized trust in insecure servers (see PoC)
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A race condition in Eclipse Jersey's SSL connector can cause critical security settings to be ignored, potentially allowing unauthorized trust.
Vulnerability
Description
In Eclipse Jersey versions 2.45, 3.0.16, and 3.1.9, a race condition exists in the default HTTPS URL connection handling that can cause critical SSL configurations — such as mutual authentication, custom key stores, trust stores, and other security settings — to be ignored [1][2]. The bug manifests when multiple concurrent requests are made; during the first GET requests that occur in parallel may fail with an SSLHandshakeException due to a PKIX path building failure until the first request completes, after which subsequent requests work correctly [2]. This indicates a lack of thread safety in the client's SSL context setup.
Attack
Vector
An attacker could exploit this by sending concurrent requests to a Jersey-based service, triggering the race condition. During this window, the client may bypass configured SSL trust verification and connect to an insecure or malicious server that would otherwise be rejected [1]. The attack does not require special privileges beyond network access to trigger parallel requests, making it accessible to any unauthenticated client.
Impact
Successful exploitation could lead to unauthorized trust in insecure servers, potentially enabling man-in-the-middle attacks, data interception, or connection to malicious endpoints. The official description notes that under certain conditions, this can result in ignoring critical SSL configurations, which may compromise mutual authentication and custom trust store validation [1].
Mitigation
The issue is fixed in Jersey releases containing the commit that addresses the concurrent SSL connector problem [2]. Users should upgrade to Jersey 3.1.9 should upgrade to 3.1.10 or later, and similar fixes exist for the 2.x and 3.0.x branches [3]. No known workarounds are documented; applying the patch is recommended. The vulnerability is not listed on CISA's Known Exploited Vulnerabilities (KEV) catalog as of the publication date.
AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.glassfish.jersey.core:jersey-clientMaven | >= 2.45, < 2.46 | 2.46 |
org.glassfish.jersey.core:jersey-clientMaven | >= 3.0.16, < 3.0.17 | 3.0.17 |
org.glassfish.jersey.core:jersey-clientMaven | >= 3.1.9, < 3.1.10 | 3.1.10 |
Affected products
2- Eclipse Foundation/Jerseyv5Range: 2.45
Patches
2b2c7ba6d388cFix possible concurrent issue with SSL & default connector (#5794)
6 files changed · +231 −16
core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java+2 −15 modified@@ -21,9 +21,6 @@ import java.net.Proxy; import java.net.URL; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; import javax.ws.rs.client.Client; @@ -290,16 +287,12 @@ public interface ConnectionFactory { * @throws java.io.IOException in case the connection cannot be provided. */ default HttpURLConnection getConnection(URL url, Proxy proxy) throws IOException { - synchronized (this){ - return (proxy == null) ? getConnection(url) : (HttpURLConnection) url.openConnection(proxy); - } + return (proxy == null) ? getConnection(url) : (HttpURLConnection) url.openConnection(proxy); } } private static class DefaultConnectionFactory implements ConnectionFactory { - private final ConcurrentHashMap<URL, Lock> locks = new ConcurrentHashMap<>(); - @Override public HttpURLConnection getConnection(final URL url) throws IOException { return connect(url, null); @@ -311,13 +304,7 @@ public HttpURLConnection getConnection(URL url, Proxy proxy) throws IOException } private HttpURLConnection connect(URL url, Proxy proxy) throws IOException { - Lock lock = locks.computeIfAbsent(url, u -> new ReentrantLock()); - lock.lock(); - try { - return (proxy == null) ? (HttpURLConnection) url.openConnection() : (HttpURLConnection) url.openConnection(proxy); - } finally { - lock.unlock(); - } + return (proxy == null) ? (HttpURLConnection) url.openConnection() : (HttpURLConnection) url.openConnection(proxy); } }
core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java+5 −1 modified@@ -84,7 +84,7 @@ public class HttpUrlConnector implements Connector { private static final String ALLOW_RESTRICTED_HEADERS_SYSTEM_PROPERTY = "sun.net.http.allowRestrictedHeaders"; // Avoid multi-thread uses of HttpsURLConnection.getDefaultSSLSocketFactory() because it does not implement a // proper lazy-initialization. See https://github.com/jersey/jersey/issues/3293 - private static final Value<SSLSocketFactory> DEFAULT_SSL_SOCKET_FACTORY = + private static final LazyValue<SSLSocketFactory> DEFAULT_SSL_SOCKET_FACTORY = Values.lazy((Value<SSLSocketFactory>) () -> HttpsURLConnection.getDefaultSSLSocketFactory()); // The list of restricted headers is extracted from sun.net.www.protocol.http.HttpURLConnection private static final String[] restrictedHeaders = { @@ -387,6 +387,10 @@ private ClientResponse _apply(final ClientRequest request) throws IOException { sniUri = request.getUri(); } + if (!DEFAULT_SSL_SOCKET_FACTORY.isInitialized() && "HTTPS".equalsIgnoreCase(sniUri.getScheme())) { + DEFAULT_SSL_SOCKET_FACTORY.get(); + } + proxy.ifPresent(clientProxy -> ClientProxy.setBasicAuthorizationHeader(request.getHeaders(), proxy.get())); uc = this.connectionFactory.getConnection(sniUri.toURL(), proxy.isPresent() ? proxy.get().proxy() : null); uc.setDoInput(true);
tests/e2e-tls/pom.xml+18 −0 modified@@ -164,6 +164,24 @@ </http.patch.addopens> </properties> </profile> + <profile> + <id>JDK_17-</id> + <activation> + <jdk>[1.8,17)</jdk> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <!-- The certificate is not working with JDK 11 --> + <excludes><exclude>**/ConcurrentHttpsUrlConnectionTest*</exclude></excludes> + </configuration> + </plugin> + </plugins> + </build> + </profile> </profiles> </project>
tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/connector/ConcurrentHttpsUrlConnectionTest.java+119 −0 added@@ -0,0 +1,119 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.tls.connector; + +import org.junit.jupiter.api.Test; + +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Client; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.MediaType; + +import java.io.InputStream; +import java.net.URL; +import java.security.KeyStore; +import java.util.Random; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManagerFactory; + +/** + * Jersey client seems to be not thread-safe: + * When the first GET request is in progress, + * all parallel requests from other Jersey client instances fail + * with SSLHandshakeException: PKIX path building failed. + * <p> + * Once the first GET request is completed, + * all subsequent requests work without error. + * <p> + * BUG 5749 + */ +public class ConcurrentHttpsUrlConnectionTest { + private static int THREAD_NUMBER = 5; + + private static volatile int responseCounter = 0; + + private static SSLContext createContext() throws Exception { + URL url = ConcurrentHttpsUrlConnectionTest.class.getResource("keystore.jks"); + KeyStore keyStore = KeyStore.getInstance("JKS"); + try (InputStream is = url.openStream()) { + keyStore.load(is, "password".toCharArray()); + } + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(keyStore, "password".toCharArray()); + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); + tmf.init(keyStore); + SSLContext context = SSLContext.getInstance("TLS"); + context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + return context; + } + + @Test + public void testSSLConnections() throws Exception { + if (THREAD_NUMBER == 1) { + System.out.println("\nThis is the working case (THREAD_NUMBER==1). Set THREAD_NUMBER > 1 to reproduce the error! \n"); + } + + final HttpsServer server = new HttpsServer(createContext()); + Executors.newFixedThreadPool(1).submit(server); + + // set THREAD_NUMBER > 1 to reproduce an issue + ExecutorService executorService2clients = Executors.newFixedThreadPool(THREAD_NUMBER); + + final ClientBuilder builder = ClientBuilder.newBuilder().sslContext(createContext()) + .hostnameVerifier(new HostnameVerifier() { + public boolean verify(String arg0, SSLSession arg1) { + return true; + } + }); + + AtomicInteger counter = new AtomicInteger(0); + + for (int i = 0; i < THREAD_NUMBER; i++) { + executorService2clients.submit(new Runnable() { + @Override + public void run() { + try { + Client client = builder.build(); + String ret = client.target("https://127.0.0.1:" + server.getPort() + "/" + new Random().nextInt()) + .request(MediaType.TEXT_HTML) + .get(new GenericType<String>() { + }); + System.out.print(++responseCounter + ". Server returned: " + ret); + } catch (Exception e) { + //get an exception here, if jersey lib is buggy and THREAD_NUMBER > 1: + //jakarta.ws.rs.ProcessingException: javax.net.ssl.SSLHandshakeException: PKIX path building failed: + e.printStackTrace(); + } finally { + System.out.println(counter.incrementAndGet()); + } + } + }); + } + + while (counter.get() != THREAD_NUMBER) { + Thread.sleep(100L); + } + server.stop(); + } +}
tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/connector/HttpsServer.java+87 −0 added@@ -0,0 +1,87 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.tls.connector; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSocket; + +class HttpsServer implements Runnable { + private final SSLServerSocket sslServerSocket; + private boolean closed = false; + + public HttpsServer(SSLContext context) throws Exception { + sslServerSocket = (SSLServerSocket) context.getServerSocketFactory().createServerSocket(0); + } + + public int getPort() { + return sslServerSocket.getLocalPort(); + } + + @Override + public void run() { + System.out.printf("Server started on port %d%n", getPort()); + while (!closed) { + SSLSocket s; + try { + s = (SSLSocket) sslServerSocket.accept(); + } catch (IOException e2) { + s = null; + } + final SSLSocket socket = s; + new Thread(new Runnable() { + public void run() { + try { + if (socket != null) { + InputStream is = new BufferedInputStream(socket.getInputStream()); + byte[] data = new byte[2048]; + int len = is.read(data); + if (len <= 0) { + throw new IOException("no data received"); + } + //System.out.printf("Server received: %s\n", new String(data, 0, len)); + PrintWriter writer = new PrintWriter(socket.getOutputStream()); + writer.println("HTTP/1.1 200 OK"); + writer.println("Content-Type: text/html"); + writer.println(); + writer.println("Hello from server!"); + writer.flush(); + writer.close(); + socket.close(); + } + } catch (Exception e1) { + e1.printStackTrace(); + } + } + }).start(); + } + } + + void stop() { + try { + closed = true; + sslServerSocket.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +}
tests/e2e-tls/src/test/resources/org/glassfish/jersey/tests/e2e/tls/connector/keystore.jks+0 −0 added
425bc883d8d6Jersey update from 3.1.3 to 3.1.4 slows down our external service res… #5746
1 file changed · +21 −1
core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java+21 −1 modified@@ -21,6 +21,9 @@ import java.net.Proxy; import java.net.URL; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; import javax.ws.rs.client.Client; @@ -295,9 +298,26 @@ default HttpURLConnection getConnection(URL url, Proxy proxy) throws IOException private static class DefaultConnectionFactory implements ConnectionFactory { + private final ConcurrentHashMap<URL, Lock> locks = new ConcurrentHashMap<>(); + @Override public HttpURLConnection getConnection(final URL url) throws IOException { - return (HttpURLConnection) url.openConnection(); + return connect(url, null); + } + + @Override + public HttpURLConnection getConnection(URL url, Proxy proxy) throws IOException { + return connect(url, proxy); + } + + private HttpURLConnection connect(URL url, Proxy proxy) throws IOException { + Lock lock = locks.computeIfAbsent(url, u -> new ReentrantLock()); + lock.lock(); + try { + return (proxy == null) ? (HttpURLConnection) url.openConnection() : (HttpURLConnection) url.openConnection(proxy); + } finally { + lock.unlock(); + } } }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
12- github.com/advisories/GHSA-7p63-w6x9-6gr7ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-12383ghsaADVISORY
- github.com/eclipse-ee4j/jersey/commit/425bc883d8d623ef8d3c448fafd36729f7741bcbghsaWEB
- github.com/eclipse-ee4j/jersey/commit/b2c7ba6d388cb9722f39073d7e82aa818fec49d5ghsaWEB
- github.com/eclipse-ee4j/jersey/pull/5749ghsaWEB
- github.com/eclipse-ee4j/jersey/pull/5794ghsaWEB
- github.com/eclipse-ee4j/jersey/releases/tag/2.46ghsaWEB
- github.com/eclipse-ee4j/jersey/releases/tag/3.0.17ghsaWEB
- github.com/eclipse-ee4j/jersey/releases/tag/3.1.10ghsaWEB
- github.com/eclipse-ee4j/jersey/releases/tag/4.0.0-M2ghsaWEB
- gitlab.eclipse.org/security/cve-assignment/-/issues/74ghsaWEB
- gitlab.eclipse.org/security/vulnerability-reports/-/issues/253ghsaWEB
News mentions
0No linked articles in our index yet.