HIGH time of check time of usemutual tls

Time Of Check Time Of Use with Mutual Tls

How Time Of Check Time Of Use Manifests in Mutual TLS

Mutual TLS (mTLS) authenticates both parties by validating X.509 certificates during the TLS handshake. A Time Of Check Time Of Use (TOCTOU) flaw appears when the application validates the client certificate only once — typically at connection establishment — and then reuses the authenticated TLS session for multiple requests without re‑validating the certificate for each request. If an attacker can obtain or hijack a valid session after the initial check, they can send malicious requests that are trusted because the server still believes the session is bound to the original client certificate.

Common code paths where this occurs include connection pooling, HTTP keep‑alive, and TLS session resumption. For example, a Go HTTP server that sets tls.Config.ClientAuth = tls.RequireAndVerifyClientCert but leaves session tickets enabled will allow a client to complete a successful handshake, receive a session ticket, and later present that ticket to resume a session. The server will skip certificate verification on resumption, accepting any data encrypted with the resumed keys.

Attackers can exploit this by:

  • Stealing a valid session ticket from a legitimate client (e.g., via side‑channel or memory dump).
  • Forcing a TLS renegotiation that reuses the original client certificate but allows the attacker to inject unauthenticated data after the check.
  • Abusing misconfigured load balancers that terminate mTLS and forward plain HTTP to backend services, trusting the front‑end’s certificate check.
This pattern maps to OWASP API Security Top 10 A2: Broken Authentication, because the authentication bound to the TLS channel is not enforced on every API call.

Mutual TLS-Specific Detection

middleBrick’s black‑box scan includes an Encryption check that probes for Mutual TLS usage and validates whether the server enforces per‑request client certificate verification. The scanner attempts a full TLS handshake with a valid client certificate, captures the session ticket (if offered), and then replays that ticket in a new connection without presenting a certificate. If the server accepts the request, middleBrick flags a potential TOCTOU condition in the Mutual TLS implementation.

Because the scan is unauthenticated and agent‑less, you only need to provide the public API URL. Example using the middleBrick CLI:

middlebrick scan https://api.example.com/health

The output will list the finding under the Encryption category, with severity, a short description, and remediation guidance. If the server does not offer session tickets or forces re‑verification on each connection, the check will pass and no finding will be reported.

Mutual TLS-Specific Remediation

The most reliable fix is to bind the client certificate validation to every request, eliminating the window where a stale session could be abused. This can be done by disabling TLS session resumption or by configuring the TLS library to verify the certificate on each resumption attempt.

Go (net/http)

tlsConfig := &tls.Config{
    ClientAuth: tls.RequireAndVerifyClientCert,
    ClientCAs:  caPool,
    // Disable session tickets to prevent resumption without re‑check
    SessionTicketsDisabled: true,
    // Optional: enforce verification on each resumption
    VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
        // Perform any additional checks (e.g., OCSP, CRL) here
        return nil
    },
}
httpServer := &http.Server{
    Addr:      "0.0.0.0:8443",
    Handler:   mux,
    TLSConfig: tlsConfig,
}
httpServer.ListenAndServeTLS("cert.pem", "key.pem")

Node.js (tls module)

const tls = require('tls');
const fs = require('fs');

const options = {
    key:  fs.readFileSync('server-key.pem'),
    cert: fs.readFileSync('server-cert.pem'),
    ca:   fs.readFileSync('ca.pem'),
    requestCert: true,
    rejectUnauthorized: true,
    // Disable session tickets and session reuse
    sessionIdContext: Buffer.from('nodejs-mtls'),
    honorCipherOrder: true,
    // Force verification on each handshake
    secureProtocol: 'TLSv1_2_method',
    sessionTimeout: 0,
};

const server = tls.createServer(options, (socket) => {
    // socket.authorized is true only if the client cert validated
    if (!socket.authorized) {
        socket.write('Handshake failed\n');
        socket.end();
        return;
    }
    // Process request knowing the cert is fresh
    socket.write('Hello authenticated client\n');
    socket.end();
});

server.listen(8443, () => {
    console.log('mTLS server listening on port 8443');
});

Java (SSLEngine)

SSLContext ctx = SSLContext.getInstance("TLSv1.2");
ctx.init(keyManagers, trustManagers, null);
SSLEngine engine = ctx.createSSLEngine();
engine.setNeedClientAuth(true);
// Disable session caching to force full handshake each time
engine.setEnableSessionCreation(false);
// Optional: enable OCSP checking via PKIX parameters
// ...

After applying these changes, re‑run middleBrick to confirm the finding is resolved. The scanner will no longer accept a resumed session without a fresh client certificate, confirming that the TOCTOU window has been closed.

Frequently Asked Questions

Does middleBrick need any credentials or agents to test Mutual TLS?
No. middleBrick performs an unauthenticated, black‑box scan by simply submitting the public API URL. It does not require agents, API keys, or access to private keys or certificates.
If my API already uses Mutual TLS, why would I still see a TOCTOU finding?
Mutual TLS only guarantees that the certificate presented during the TLS handshake is valid. If the server allows session resumption or connection pooling without re‑validating the certificate for each request, an attacker who captures or reuses a valid session can bypass the check. middleBrick detects exactly this gap.