Jwt Misconfiguration with Mutual Tls
How Jwt Misconfiguration Manifests in Mutual Tls
Mutual TLS (mTLS) establishes a two-way authentication channel where both client and server present certificates to verify identity. When JWTs are used within this context, misconfigurations create attack vectors that exploit the trust model.
A common vulnerability occurs when JWTs are signed using the client certificate's private key, but the server fails to verify the certificate chain before processing the token. Consider this flawed implementation:
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
app.post('/api/secure', (req, res) => {
const cert = req.clientCert; // mTLS certificate
const payload = { sub: cert.subject }; // extract identity from cert
// Vulnerable: no certificate validation before JWT creation
const token = jwt.sign(payload, cert.privateKey, {
algorithm: 'RS256'
});
// Process token without verifying cert chain
const decoded = jwt.verify(token, cert.publicKey);
res.json({ message: 'Authenticated', identity: decoded.sub });
});
This pattern allows an attacker to present any certificate that can establish a TLS connection, then forge JWTs using the certificate's private key. The server trusts the certificate for TLS but doesn't validate it's from a trusted CA before using it for JWT signing.
Another critical misconfiguration involves token replay across different mTLS sessions. When JWTs are issued based on mTLS authentication but lack proper binding to the certificate's unique identifiers:
public String createJwtWithCertificateInfo(X509Certificate clientCert) {
String certSerial = clientCert.getSerialNumber().toString();
String certIssuer = clientCert.getIssuerDN().getName();
// Vulnerable: only includes serial and issuer, not full cert hash
Map<String, Object> claims = new HashMap<>();
claims.put("cert_serial", certSerial);
claims.put("cert_issuer", certIssuer);
// Attacker can reuse token if they obtain the private key
return Jwts.builder()
.setClaims(claims)
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
}
An attacker who compromises a client certificate's private key can create valid JWTs that will be accepted by any service expecting that certificate, even if the actual certificate isn't presented during the TLS handshake.
Time-based token expiration without certificate revocation checking creates another vulnerability. Consider:
func handleRequest(w http.ResponseWriter, r *http.Request) {
cert, err := getClientCert(r.TLS);
if err != nil { http.Error(w, "No cert", 401); return }
token := extractJWTFromHeader(r);
claims, err := jwt.ParseWithCert(token, cert);
// Vulnerable: no check if certificate is revoked
if err == nil {
w.Write([]byte("Access granted"));
}
}
If the client certificate is revoked due to compromise but the JWT hasn't expired, the server will still accept the token until expiration, creating a window of vulnerability.
Mutual Tls-Specific Detection
Detecting JWT misconfigurations in mTLS environments requires examining both the certificate handling and token processing logic. The key indicators include:
Certificate Validation Gaps - Tools should verify that servers validate the complete certificate chain before accepting JWTs signed with client certificates. This includes checking:
- Certificate is issued by a trusted CA in the server's trust store
- Certificate hasn't been revoked (via OCSP or CRL)
- Certificate is within its validity period
- Certificate matches expected client identity
Token Binding Verification - JWTs in mTLS contexts should be cryptographically bound to the certificate used for authentication. Detection tools should check for:
# Check for certificate thumbprint binding in JWT claims
# A properly bound token includes a certificate fingerprint
THUMBPRINT=$(openssl x509 -in client.crt -noout -fingerprint)
# The JWT should contain this exact thumbprint in a custom claim
# Missing or mismatched thumbprint indicates potential misconfiguration
Mutual Authentication Bypass - Scan for endpoints that accept JWTs without requiring the client certificate for token processing:
# Test if endpoint accepts token without client cert
curl -H "Authorization: Bearer $TOKEN" https://api.example.com/secure
# Should require client cert, but misconfigured endpoints may not
Certificate Reuse Detection - Tools should identify if JWTs can be reused across different mTLS sessions by:
- Capturing a valid JWT from one mTLS session
- Attempting to use it with a different client certificate
- Checking if the server accepts the token without certificate validation
middleBrick's mTLS-Specific Scanning - middleBrick's black-box scanner tests mTLS endpoints by:
- Attempting connections with both valid and expired client certificates
- Checking if JWTs are accepted without proper certificate validation
- Testing token replay across different certificate contexts
- Verifying certificate revocation checking is implemented
The scanner provides a security score and detailed findings including specific certificate validation failures and JWT binding issues.
Mutual Tls-Specific Remediation
Remediating JWT misconfigurations in mTLS environments requires implementing proper certificate validation and token binding. Here are specific fixes for common vulnerabilities:
Certificate Chain Validation - Always validate the complete certificate chain before processing JWTs:
public boolean validateClientCertificate(X509Certificate cert) {
try {
// Validate against trusted CAs
cert.verify(trustedCAPublicKey);
// Check revocation status
if (isCertificateRevoked(cert)) {
return false;
}
// Verify certificate is within validity period
if (!cert.isValid()) {
return false;
}
return true;
} catch (Exception e) {
return false;
}
}
// Use in JWT processing
X509Certificate clientCert = getClientCertFromTLS();
if (!validateClientCertificate(clientCert)) {
throw new SecurityException("Invalid client certificate");
}
Certificate Binding in JWT Claims - Include certificate-specific information in JWT claims to prevent token reuse:
import hashlib
import jwt
from datetime import datetime, timedelta
def create_bound_jwt(client_cert, private_key):
# Extract certificate fingerprint
cert_der = client_cert.dump()
cert_fingerprint = hashlib.sha256(cert_der).hexdigest()
# Include certificate binding in claims
claims = {
'sub': client_cert.get_subject().CN,
'cert_fingerprint': cert_fingerprint,
'iat': datetime.utcnow(),
'exp': datetime.utcnow() + timedelta(hours=1)
}
# Sign with server private key (not client cert)
token = jwt.encode(claims, private_key, algorithm='RS256')
return token
Certificate Revocation Checking - Implement real-time revocation checking:
public bool IsCertificateRevoked(X509Certificate2 cert)
{
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
if (!chain.Build(cert))
{
foreach (X509ChainElement element in chain.ChainElements)
{
if (element.ChainElementStatus.Any(status =>
status.Status == X509ChainStatusFlags.Revoked))
{
return true;
}
}
}
return false;
}
Secure Token Issuance - Use server-side signing rather than client certificate private keys:
func IssueSecureToken(clientCert *x509.Certificate, serverKey *rsa.PrivateKey) (string, error) {
// Validate certificate first
if !validateCertificate(clientCert) {
return "", errors.New("invalid certificate")
}
// Create claims with certificate binding
claims := jwt.MapClaims{
"sub": clientCert.Subject.CommonName,
"cert_serial": clientCert.SerialNumber.String(),
"cert_fingerprint": fingerprintCertificate(clientCert),
"iat": time.Now().Unix(),
"exp": time.Now().Add(1 * time.Hour).Unix(),
}
// Sign with server key, not client cert
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
return token.SignedString(serverKey)
}
middleBrick Integration - After implementing these fixes, use middleBrick's CLI to verify remediation:
# Scan your mTLS endpoint
middlebrick scan https://api.example.com/secure \
--client-cert client.crt \
--client-key client.key
# Check the report for certificate validation findings
# The tool will verify:
# - Certificate chain validation
# - Token binding to certificate
# - Revocation checking implementation
# - Proper mTLS authentication requirements
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |