{"id":"9db1fd5f95821ac9","repo":"brianc/node-postgres","slug":"sasl-scram-server-first-message-nonce-must-only","errorCode":null,"errorMessage":"SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce must only contain printable characters","messagePattern":"SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce must only contain printable characters","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/pg/lib/crypto/sasl.js","lineNumber":199,"sourceCode":"    text.split(',').map((attrValue) => {\n      if (!/^.=/.test(attrValue)) {\n        throw new Error('SASL: Invalid attribute pair entry')\n      }\n      const name = attrValue[0]\n      const value = attrValue.substring(2)\n      return [name, value]\n    })\n  )\n}\n\nfunction parseServerFirstMessage(data) {\n  const attrPairs = parseAttributePairs(data)\n\n  const nonce = attrPairs.get('r')\n  if (!nonce) {\n    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce missing')\n  } else if (!isPrintableChars(nonce)) {\n    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce must only contain printable characters')\n  }\n  const salt = attrPairs.get('s')\n  if (!salt) {\n    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: salt missing')\n  } else if (!isBase64(salt)) {\n    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: salt must be base64')\n  }\n  const iterationText = attrPairs.get('i')\n  if (!iterationText) {\n    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration missing')\n  } else if (!/^[1-9][0-9]*$/.test(iterationText)) {\n    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: invalid iteration count')\n  }\n  const iteration = parseInt(iterationText, 10)\n\n  return {\n    nonce,\n    salt,","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/brianc/node-postgres/blob/c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711/packages/pg/lib/crypto/sasl.js#L181-L217","documentation":"Thrown by parseServerFirstMessage (sasl.js:198-199) when the server's nonce (r= value) contains characters outside the printable ASCII range defined by RFC 5802 (%x21-2B / %x2D-7E, excluding comma). The isPrintableChars function validates each byte; non-printable or non-ASCII bytes in the nonce could indicate corruption or a deliberately crafted malicious response. This is a protocol-conformance and safety guard.","triggerScenarios":"The server's r= attribute contains control characters, high-byte (>0x7E) characters, spaces, or commas. The isPrintableChars check at sasl.js:150-158 examines every character code.","commonSituations":"Wire-level data corruption introducing non-printable bytes. A malicious or buggy server injecting invalid nonce characters. Extremely rare with compliant PostgreSQL servers.","solutions":["Ensure a clean, unproxied connection to the PostgreSQL server.","Enable SSL to protect the authentication exchange from corruption or injection.","If using a custom PostgreSQL-compatible server, verify its SCRAM nonce generation uses only printable ASCII."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await client.connect();\n} catch (err) {\n  if (/nonce must only contain printable characters/i.test(err.message)) {\n    console.error('SCRAM nonce contains non-printable bytes — corruption or malicious server.');\n  }\n  throw err;\n}","preventionTips":["Use SSL/TLS to protect the SCRAM exchange from corruption or injection.","Verify the server's SCRAM nonce generation produces printable ASCII only.","Investigate network infrastructure for data integrity issues if this recurs."],"tags":["sasl","protocol","security","validation","authentication"],"analyzedSha":"c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711","analyzedAt":"2026-08-03T18:47:28.334Z","schemaVersion":2}