HIGH cryptographic failuresmutual tls

Cryptographic Failures with Mutual Tls

How Cryptographic Failures Manifests in Mutual TLS

Mutual TLS (mTLS) adds client‑certificate authentication to the standard TLS handshake, but cryptographic weaknesses can still appear in the handshake parameters, certificate validation logic, or key‑exchange algorithms. Common mTLS‑specific failure patterns include:

  • Weak or deprecated protocol versions – allowing TLS 1.0/1.1 or SSLv3 enables attacks such as POODLE (CVE‑2014‑3566) or BEAST (CVE‑2011‑3389). In mTLS this also lets an attacker force a downgrade and then impersonate either party.
  • Weak cipher suites – enabling CBC‑mode ciphers with MAC‑then‑Encrypt (e.g., TLS_RSA_WITH_AES_128_CBC_SHA) opens the door to Lucky‑13 style timing attacks. Export‑grade or NULL ciphers can be negotiated if the server does not enforce strong suites.
  • Insufficient certificate validation – setting verification to none or optional allows a client to connect without presenting a valid client cert, or a server to accept a self‑signed or expired cert, enabling impersonation (see CVE‑2016‑2107, the “SSL/TLS MITM” scenario).
  • Anonymous Diffie‑Hellman (DH) or RSA key exchange – using TLS_DH_anon_* suites provides no authentication, defeating the purpose of mTLS.
  • Improper key size – RSA keys < 2048 bits or elliptic‑curve curves with insufficient security (e.g., secp192r1) can be factored or solved via discrete log attacks.

These issues appear in the code paths where the TLS configuration is built. For example, in Go’s crypto/tls package, the Config fields MinVersion, CipherSuites, ClientAuth, and RootCAs directly control the handshake. In Node.js, the tls.createSecureContext options minVersion, ciphers, requestCert, and rejectUnauthorized play the same role. Misconfiguring any of these fields leads to the cryptographic failures described above.

Mutual TLS‑Specific Detection

middleBrick performs a black‑box scan of the API endpoint’s unauthenticated surface. During the scan it:

  • Initiates a TLS handshake and records the protocol version offered by the server.
  • Lists the cipher suites the server is willing to negotiate.
  • Checks whether the server requests a client certificate (CertificateRequest message) and whether it validates the presented client cert.
  • Validates the server’s certificate chain against trusted roots, checks expiration, and verifies hostname matching.
  • Flags the use of TLS < 1.2, weak ciphers (CBC‑mode, export, NULL, anonymous DH), self‑signed or expired certs, and missing client‑cert validation.

For example, running the middleBrick CLI against an API yields a finding such as:

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

[+] Cryptographic Failures (Mutual TLS)
    - Protocol version: TLS 1.0 (should be >= TLS 1.2)
    - Cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA (CBC‑mode, vulnerable to Lucky‑13)
    - Server cert: self‑signed, expired 2022-05-01
    - ClientAuth: none (server does not request or validate client cert)

The finding includes severity, remediation guidance, and maps to OWASP API Security Top 10 (M9 – Improper Inventory Management is unrelated; the finding maps to the broader Cryptographic Failures category). Because the scan is unauthenticated, it reveals exactly what an attacker can observe without any credentials, making it suitable for detecting misconfigurations in production‑exposed mTLS endpoints.

Mutual TLS‑Specific Remediation

Fixing cryptographic failures in mTLS involves tightening the TLS configuration on both client and server sides. Below are language‑specific examples that enforce TLS 1.2+, strong cipher suites, and proper certificate validation.

Go (crypto/tls)

import (
	"crypto/tls"
)

func secureTLSConfig() *tls.Config {
	return &tls.Config{
		MinVersion: tls.VersionTLS12,
		CipherSuites: []uint16{
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
			tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
		},
		PreferServerCipherSuites: true,
		ClientAuth: tls.RequireAndVerifyClientCert,
		ClientCAs: loadClientCAPool(), // function that returns *x509.CertPool
	}
}

func loadClientCAPool() *x509.CertPool {
	pool := x509.NewCertPool()
	// Add trusted client CA certificates
	pool.AppendCertsFromPEM(clientCAPEM)
	return pool
}

Node.js (tls module)

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

const options = {
  minVersion: 'TLSv1.2',
  ciphers: 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256',
  honorCipherOrder: true,
  requestCert: true,          // ask for client cert
  rejectUnauthorized: true,   // abort if client cert is not trusted
  ca: fs.readFileSync('client-ca.pem'), // trusted client CAs
};

const server = tls.createServer(options, (socket) => {
  socket.write('welcome\n');
  socket.end();
});

server.listen(8443);

Python (ssl module)

import ssl
import socket

context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.minimum_version = ssl.TLSVersion.TLSv1_2
context.maximum_version = ssl.TLSVersion.TLSv1_3
context.set_ciphers('ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305')
context.verify_mode = ssl.CERT_REQUIRED
context.load_verify_locations(cafile='client-ca.pem')

# For a server that also authenticates clients:
server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
server_context.minimum_version = ssl.TLSVersion.TLSv1_2
server_context.set_ciphers('ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305')
server_context.verify_mode = ssl.CERT_REQUIRED
server_context.load_verify_locations(cafile='client-ca.pem')
server_context.load_cert_chain(certfile='server-cert.pem', keyfile='server-key.pem')

# Wrap socket
with socket.create_connection(('api.example.com', 443)) as sock:
    with context.wrap_socket(sock, server_hostname='api.example.com') as ssock:
        print(ssock.version())

After applying these changes, re‑run middleBrick to confirm that the findings disappear and the security score improves.

Frequently Asked Questions

What is the difference between TLS and Mutual TLS?
Standard TLS authenticates only the server to the client using the server’s certificate. Mutual TLS adds a second step where the client presents a certificate that the server validates, authenticating both ends of the connection.
How often should I rotate TLS certificates in a Mutual TLS setup?
Rotate TLS certificates at least every 90 days, or sooner if you suspect key compromise. Automated renewal with tools like cert-manager or HashiCorp Vault helps maintain fresh keys without downtime.