mongodb/node-mongodb-native · error · MongoRuntimeError
Server returned an invalid signature
Error message
Server returned an invalid signature
What it means
Thrown by continueScramConversation (scram.ts:189) when the server's final SASL signature (parsedResponse.v) does not match the signature the client computed from its salted password. SCRAM uses this server signature to authenticate the server to the client; a mismatch means the party you are talking to does not hold the Server Key derived from your password. Raised as MongoRuntimeError.
Solutions
- Enable TLS/SSL on the connection (tls=true) to prevent tampering
- Verify the hostname and server certificate against the deployment you expect
- Remove proxies/load balancers that may alter the SASL payload
- Confirm the cluster endpoint is the legitimate MongoDB deployment
Example fix
// before const uri = 'mongodb://user:pass@cluster/?tls=false'; // after const uri = 'mongodb://user:pass@cluster/?tls=true&tlsCAFile=/etc/ssl/mongo-ca.pem';
Defensive patterns
Strategy: try-catch
Try / catch
try {
await client.connect();
} catch (err) {
if (err instanceof MongoRuntimeError && /invalid signature/.test(err.message)) {
// potential MITM or spoofed server; do not fall back to insecure transport
alertSecurityTeam(err);
}
throw err;
} Prevention
- Always enable TLS (tls=true) and pin a trusted CA via tlsCAFile
- Verify the server hostname/certificate matches your deployment
- Remove TLS-terminating proxies that break end-to-end authentication integrity
When it happens
Trigger: The client computed Server Key via HMAC and PBKDF2, then compared the server's returned 'v' value with a constant-time compareDigest; the two differed. This happens when the server is not the legitimate password-holding server, the exchange was tampered with, or (less commonly) the wire payload was corrupted.
Common situations: A man-in-the-middle intercepting the SCRAM exchange without knowing the password. A buggy proxy/gateway corrupting the final SASL payload. Connecting to a spoofed/misconfigured server. TLS disabled on an untrusted network allowing active tampering.
Related errors
- Server returned an invalid iteration count
- Server returned an invalid nonce
- Auth mechanism SCRAM-SHA-1 is not supported in FIPS mode
- AuthContext must contain a valid nonce property
- AuthContext must provide credentials.
AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11).
Data as JSON: /api/errors/1cfea67daf0769d6.
Report an issue: GitHub.
Appendix: source
Thrown at src/cmap/auth/scram.ts:189
const payloadString = ByteUtils.toUTF8(payload.buffer, 0, payload.position, false);
const authMessage = [firstMessage, payloadString, withoutProof].join(',');
const clientSignature = await HMAC(cryptoMethod, storedKey, authMessage);
const clientProof = `p=${xor(clientKey, clientSignature)}`;
const clientFinal = [withoutProof, clientProof].join(',');
const serverSignature = await HMAC(cryptoMethod, serverKey, authMessage);
const saslContinueCmd = {
saslContinue: 1,
conversationId: response.conversationId,
payload: new Binary(ByteUtils.fromUTF8(clientFinal))
};
const r = await connection.command(ns(`${db}.$cmd`), saslContinueCmd, undefined);
const parsedResponse = parsePayload(r.payload);
if (!compareDigest(ByteUtils.fromBase64(parsedResponse.v), serverSignature)) {
throw new MongoRuntimeError('Server returned an invalid signature');
}
if (r.done !== false) {
// If the server sends r.done === true we can save one RTT
return;
}
const retrySaslContinueCmd = {
saslContinue: 1,
conversationId: r.conversationId,
payload: ByteUtils.allocate(0)
};
await connection.command(ns(`${db}.$cmd`), retrySaslContinueCmd, undefined);
}
function parsePayload(payload: Binary) {
const payloadStr = ByteUtils.toUTF8(payload.buffer, 0, payload.position, false);View on GitHub (pinned to dce7939f86)