{"id":"3c20340712aaabfd","repo":"brianc/node-postgres","slug":"sasl-scram-server-first-message-iteration-count","errorCode":null,"errorMessage":"SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration count ${sv.iteration} exceeds scramMaxIterations of ${scramMaxIterations}","messagePattern":"SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration count (.+?) exceeds scramMaxIterations of (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/pg/lib/crypto/sasl.js","lineNumber":88,"sourceCode":"    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a non-empty string')\n  }\n  if (typeof serverData !== 'string') {\n    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: serverData must be a string')\n  }\n\n  const sv = parseServerFirstMessage(serverData)\n\n  if (!sv.nonce.startsWith(session.clientNonce)) {\n    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce does not start with client nonce')\n  } else if (sv.nonce.length === session.clientNonce.length) {\n    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce is too short')\n  }\n\n  const scramMaxIterations =\n    typeof session.scramMaxIterations === 'number' ? session.scramMaxIterations : DEFAULT_MAX_SCRAM_ITERATIONS\n  // a value of 0 disables the iteration count check\n  if (scramMaxIterations !== 0 && sv.iteration > scramMaxIterations) {\n    throw new Error(\n      'SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration count ' +\n        sv.iteration +\n        ' exceeds scramMaxIterations of ' +\n        scramMaxIterations\n    )\n  }\n\n  const clientFirstMessageBare = 'n=*,r=' + session.clientNonce\n  const serverFirstMessage = 'r=' + sv.nonce + ',s=' + sv.salt + ',i=' + sv.iteration\n\n  // without channel binding:\n  let channelBinding = stream ? 'eSws' : 'biws' // 'y,,' or 'n,,', base64-encoded\n\n  // override if channel binding is in use:\n  if (session.mechanism === 'SCRAM-SHA-256-PLUS') {\n    const peerCert = stream.getPeerCertificate().raw\n    let hashName = signatureAlgorithmHashFromCertificate(peerCert)\n    if (hashName === 'MD5' || hashName === 'SHA-1') hashName = 'SHA-256'","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/brianc/node-postgres/blob/c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711/packages/pg/lib/crypto/sasl.js#L70-L106","documentation":"Thrown during SCRAM session continuation (sasl.js:87-94) when the server's requested PBKDF2 iteration count exceeds the client's scramMaxIterations limit (default 100000, configurable via Client option scramMaxIterations; 0 disables the check). The iteration count determines how many PBKDF2 rounds are used to derive the salted key — an absurdly high value could be a denial-of-service vector (each authentication would take extremely long). The client refuses to honor iterations above the cap to bound CPU cost during login. This guard was added as a security hardening measure.","triggerScenarios":"The server sends an i= value greater than the client's cap. With the default cap of 100000, a server configured with scram_iterations=200000 or higher would trigger it. Setting new Client({ scramMaxIterations: 10000 }) lowers the bar further.","commonSituations":"A PostgreSQL server (14+) with scram_iterations set very high for extra brute-force resistance. A misconfigured server sending a malformed iteration count. A development/test server with an artificially high value.","solutions":["Raise the client cap: new Client({ scramMaxIterations: 200000 }) to match the server's scram_iterations.","Lower the server's scram_iterations to 100000 or below: ALTER SYSTEM SET scram_iterations = 100000.","Set scramMaxIterations: 0 to disable the check entirely (not recommended — removes the DoS protection)."],"exampleFix":"// before\nconst client = new Client({ /* scramMaxIterations defaults to 100000 */ });\n// server has scram_iterations=200000\n\n// after\nconst client = new Client({ scramMaxIterations: 200000 });","handlingStrategy":"validation","validationCode":"// Before constructing the client, check or align scramMaxIterations\nconst SERVER_SCRAM_ITERATIONS = 200000; // from your server config\nconst client = new Client({\n  scramMaxIterations: SERVER_SCRAM_ITERATIONS,\n});","typeGuard":null,"tryCatchPattern":"try {\n  await client.connect();\n} catch (err) {\n  if (/iteration count.*exceeds scramMaxIterations/i.test(err.message)) {\n    // Extract the server's count from the message and raise the client cap\n    const match = err.message.match(/iteration count (\\d+)/);\n    const serverIters = match ? parseInt(match[1], 10) : 200000;\n    client2 = new Client({ ...config, scramMaxIterations: serverIters });\n    await client2.connect();\n  } else {\n    throw err;\n  }\n}","preventionTips":["Check the server's scram_iterations setting and set the client's scramMaxIterations to match or exceed it.","Do not set scramMaxIterations to 0 unless you understand the DoS implications.","Document the scram_iterations <-> scramMaxIterations relationship in your deployment config."],"tags":["sasl","security","dos","authentication","config"],"analyzedSha":"c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711","analyzedAt":"2026-08-03T18:47:28.334Z","schemaVersion":2}