{"id":"de1276079e381838","repo":"brianc/node-postgres","slug":"security-warning-using-sslmode-verify-ca-requires","errorCode":null,"errorMessage":"SECURITY WARNING: Using sslmode=verify-ca requires specifying a CA with sslrootcert. If a public CA is used, verify-ca allows connections to a server that somebody else may have registered with the CA, making you vulnerable to Man-in-the-Middle attacks. Either specify a custom CA certificate with sslrootcert parameter or use sslmode=verify-full for proper security.","messagePattern":"SECURITY WARNING: Using sslmode=verify-ca requires specifying a CA with sslrootcert\\. If a public CA is used, verify-ca allows connections to a server that somebody else may have registered with the CA, making you vulnerable to Man-in-the-Middle attacks\\. Either specify a custom CA certificate with sslrootcert parameter or use sslmode=verify-full for proper security\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/pg-connection-string/index.js","lineNumber":127,"sourceCode":"        config.ssl = false\n        break\n      }\n      case 'prefer': {\n        config.ssl.rejectUnauthorized = false\n        break\n      }\n      case 'require': {\n        if (config.sslrootcert) {\n          // If a root CA is specified, behavior of `sslmode=require` will be the same as that of `verify-ca`\n          config.ssl.checkServerIdentity = function () {}\n        } else {\n          config.ssl.rejectUnauthorized = false\n        }\n        break\n      }\n      case 'verify-ca': {\n        if (!config.ssl.ca) {\n          throw new Error(\n            'SECURITY WARNING: Using sslmode=verify-ca requires specifying a CA with sslrootcert. If a public CA is used, verify-ca allows connections to a server that somebody else may have registered with the CA, making you vulnerable to Man-in-the-Middle attacks. Either specify a custom CA certificate with sslrootcert parameter or use sslmode=verify-full for proper security.'\n          )\n        }\n        config.ssl.checkServerIdentity = function () {}\n        break\n      }\n      case 'verify-full': {\n        break\n      }\n    }\n  } else {\n    switch (config.sslmode) {\n      case 'disable': {\n        config.ssl = false\n        break\n      }\n      case 'prefer':\n      case 'require':","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/brianc/node-postgres/blob/c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711/packages/pg-connection-string/index.js#L109-L145","documentation":"Thrown in libpq-compatibility mode (uselibpqCompat or uselibpqcompat enabled) when the connection string specifies sslmode=verify-ca but no sslrootcert parameter is provided. In libpq semantics, verify-ca only checks that the server certificate chains to a trusted CA but does NOT verify the hostname — so without pinning a specific CA via sslrootcert, a public CA could have issued a certificate for an attacker's server, enabling man-in-the-middle attacks. The guard at index.js:126 checks !config.ssl.ca and throws to prevent silently using a dangerously weak configuration.","triggerScenarios":"A connection string containing uselibpqcompat=true&sslmode=verify-ca with no sslrootcert= parameter. The code path at index.js:125-130 executes inside the libpq-compat sslmode switch and throws because config.ssl.ca is undefined.","commonSituations":"Migrating from sslmode=verify-full to verify-ca for an internal CA but forgetting to ship/pin the CA certificate. Copying a libpq/psql config that relies on the system CA store (which node-postgres does not use by default the same way). Configuring an internal PostgreSQL server with a self-signed CA but omitting the root cert path.","solutions":["Add sslrootcert=/path/to/your-ca.pem to the connection string so the client pins your specific CA.","Switch to sslmode=verify-full which performs both CA chain and hostname verification and is the recommended secure default.","If you genuinely need verify-ca semantics, ensure the sslrootcert file exists at the given path and is readable by the Node process."],"exampleFix":"// before\nconst connStr = 'postgres://host/db?sslmode=verify-ca&uselibpqcompat=true';\n\n// after\nconst connStr = 'postgres://host/db?sslmode=verify-ca&uselibpqcompat=true&sslrootcert=/etc/ssl/certs/pg-ca.pem';\n// or (recommended)\nconst connStr = 'postgres://host/db?sslmode=verify-full&uselibpqcompat=true&sslrootcert=/etc/ssl/certs/pg-ca.pem';","handlingStrategy":"validation","validationCode":"function validateSslConfig(connStr) {\n  const url = new URL(connStr, 'postgres://base');\n  const sslmode = url.searchParams.get('sslmode');\n  const sslrootcert = url.searchParams.get('sslrootcert');\n  const useLibpqCompat = url.searchParams.get('uselibpqcompat') === 'true';\n  if (useLibpqCompat && sslmode === 'verify-ca' && !sslrootcert) {\n    throw new Error(\n      'sslmode=verify-ca requires sslrootcert. Use verify-full or provide a CA cert path.'\n    );\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  const config = parseIntoClientConfig(connStr);\n} catch (err) {\n  if (/verify-ca requires specifying a CA/i.test(err.message)) {\n    // Either add sslrootcert or switch to verify-full\n    connStr = connStr.replace('sslmode=verify-ca', 'sslmode=verify-full');\n    config = parseIntoClientConfig(connStr);\n  } else {\n    throw err;\n  }\n}","preventionTips":["Prefer sslmode=verify-full for all connections unless you have a specific reason for verify-ca.","Always pin a custom CA with sslrootcert for internal infrastructure rather than relying on public CAs.","Add a config linter or startup check that rejects verify-ca without sslrootcert in production."],"tags":["ssl","security","mitm","config","connection-string"],"analyzedSha":"c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711","analyzedAt":"2026-08-03T18:47:28.334Z","schemaVersion":2}