MCP server post-quantum security: ML-KEM, hybrid TLS, and quantum-safe authentication
NIST standardized the first post-quantum cryptography algorithms in 2024. For MCP servers handling long-lived data, the migration window has already opened: "harvest now, decrypt later" attacks mean traffic captured today can be decrypted once a cryptographically relevant quantum computer exists.
What's at risk in current MCP server deployments
A quantum computer running Shor's algorithm breaks RSA and elliptic curve cryptography (ECDSA, ECDH) in polynomial time. Current MCP server deployments typically use:
- TLS 1.3 with X25519 ECDH key exchange — vulnerable to harvest-now-decrypt-later on the key exchange, though forward secrecy limits the exposure window per session
- RS256 JWT signatures (RSA-2048 or RSA-4096) — vulnerable; an attacker who captures a JWT and later has a quantum computer can forge signatures retroactively
- ECDSA certificates (P-256, P-384) — vulnerable to quantum signature forgery, enabling CA impersonation
For most MCP servers today, the threat horizon is 10-15 years. But MCP servers that handle data with a confidentiality requirement beyond that horizon (healthcare records, legal documents, government data) need to start the migration now.
Pattern 1: Hybrid key exchange in TLS 1.3
NIST's ML-KEM (formerly Kyber) is the standardized post-quantum key encapsulation mechanism. The immediate migration path is a hybrid cipher suite that combines X25519 with ML-KEM — classically secure today and quantum-safe for the future. Both Chrome and Node.js 22+ support X25519Kyber768Draft00:
// Node.js 22+ with --experimental-network-inspection
// Set preferred cipher for outbound TLS connections
const https = require('https');
const tls = require('tls');
const agent = new https.Agent({
// Prefer hybrid X25519+ML-KEM key exchange
// Falls back to X25519 if server doesn't support PQC
ciphers: [
'TLS_AES_256_GCM_SHA384', // TLS 1.3 (mandatory)
'TLS_CHACHA20_POLY1305_SHA256' // TLS 1.3 (mandatory)
].join(':'),
// Node.js 22 supports X25519Kyber768 via --enable-fips
// or via openssl.cnf with provider = oqsprovider
ecdhCurve: 'X25519Kyber768:X25519:P-256',
});
// For your server's inbound TLS (Caddy/nginx handles this automatically
// on modern versions — verify with: openssl s_client -connect host:443
// and look for "Server Temp Key: X25519Kyber768")
For Caddy (used in the SkillAudit factory deployment), post-quantum TLS is automatic from Caddy 2.9+ — no configuration needed. Check with:
curl -v --tlsv1.3 https://skillaudit.dev 2>&1 | grep "SSL connection"
Pattern 2: Post-quantum JWT signatures
NIST's ML-DSA (formerly CRYSTALS-Dilithium) is the standardized post-quantum digital signature algorithm. For JWT tokens, the jose library supports ML-DSA via the OQS provider in OpenSSL 3.x:
// Using node-oqs (OpenQuantumSafe bindings)
import { generateKeyPair, sign, verify } from 'node-oqs';
// Generate ML-DSA-65 key pair (NIST security level 3)
const { publicKey, privateKey } = await generateKeyPair('ML-DSA-65');
// Sign a JWT payload (custom JWT — jose doesn't yet support ML-DSA natively)
const header = Buffer.from(JSON.stringify({ alg: 'ML-DSA-65', typ: 'JWT' })).toString('base64url');
const payload = Buffer.from(JSON.stringify({ sub: userId, iat: Math.floor(Date.now() / 1000), exp: ... })).toString('base64url');
const signature = sign(`${header}.${payload}`, privateKey);
const token = `${header}.${payload}.${Buffer.from(signature).toString('base64url')}`;
// Verification
const [h, p, s] = token.split('.');
const valid = verify(`${h}.${p}`, Buffer.from(s, 'base64url'), publicKey);
This is a forward-looking pattern — most MCP server deployments should stay on RS256 today and plan the migration. The key preparatory step is crypto-agility: ensure your JWT library can be swapped without changing every token verification call site.
Pattern 3: Inventory and prioritize quantum-vulnerable usage
Before migrating, inventory all cryptographic operations in the MCP server codebase. A script to find RSA and EC key usage:
#!/bin/bash echo "=== RSA key operations ===" grep -rn "createSign\|RSA\|rsa\|RS256\|RS384\|RS512" src/ --include="*.js" --include="*.ts" echo "=== EC key operations ===" grep -rn "createECDH\|ECDH\|ECDSA\|ES256\|ES384\|ES512\|P-256\|P-384\|secp256k1" src/ --include="*.js" echo "=== TLS configuration ===" grep -rn "secureProtocol\|ciphers\|ecdhCurve\|createServer\|createSecureContext" src/ --include="*.js" echo "=== Certificate loading ===" grep -rn "readFileSync.*\\.pem\|readFileSync.*\\.crt\|readFileSync.*\\.key" src/ --include="*.js"
Prioritize migration based on data sensitivity and key lifetime:
- Immediate: Long-lived data encryption keys (encrypt-at-rest) — use ML-KEM now
- 2026-2027: TLS certificate keys — switch to hybrid ECDSA+ML-DSA when Let's Encrypt supports it
- 2028+: JWT signing keys — migrate to ML-DSA when toolchain support matures
SkillAudit and post-quantum readiness
SkillAudit's maintenance axis tracks cryptographic library versions and flags usage of deprecated algorithms. As the NIST PQC standards mature through 2026-2027, SkillAudit will add a dedicated post-quantum readiness check to the security axis — flagging RSA key sizes below 3072 bits and ECDH-only key exchanges without a PQC hybrid. Run an audit to see your current cryptographic posture.