brianc/node-postgres · critical · Error

SECURITY WARNING: Using sslmode=verify-ca requires specifyin

Error message

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.

What it means

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.

Source

Thrown at packages/pg-connection-string/index.js:127

        config.ssl = false
        break
      }
      case 'prefer': {
        config.ssl.rejectUnauthorized = false
        break
      }
      case 'require': {
        if (config.sslrootcert) {
          // If a root CA is specified, behavior of `sslmode=require` will be the same as that of `verify-ca`
          config.ssl.checkServerIdentity = function () {}
        } else {
          config.ssl.rejectUnauthorized = false
        }
        break
      }
      case 'verify-ca': {
        if (!config.ssl.ca) {
          throw new Error(
            '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.'
          )
        }
        config.ssl.checkServerIdentity = function () {}
        break
      }
      case 'verify-full': {
        break
      }
    }
  } else {
    switch (config.sslmode) {
      case 'disable': {
        config.ssl = false
        break
      }
      case 'prefer':
      case 'require':

View on GitHub (pinned to c5e8c9a57b)

Solutions

  1. Add sslrootcert=/path/to/your-ca.pem to the connection string so the client pins your specific CA.
  2. Switch to sslmode=verify-full which performs both CA chain and hostname verification and is the recommended secure default.
  3. If you genuinely need verify-ca semantics, ensure the sslrootcert file exists at the given path and is readable by the Node process.

Example fix

// before
const connStr = 'postgres://host/db?sslmode=verify-ca&uselibpqcompat=true';

// after
const connStr = 'postgres://host/db?sslmode=verify-ca&uselibpqcompat=true&sslrootcert=/etc/ssl/certs/pg-ca.pem';
// or (recommended)
const connStr = 'postgres://host/db?sslmode=verify-full&uselibpqcompat=true&sslrootcert=/etc/ssl/certs/pg-ca.pem';
Defensive patterns

Strategy: validation

Validate before calling

function validateSslConfig(connStr) {
  const url = new URL(connStr, 'postgres://base');
  const sslmode = url.searchParams.get('sslmode');
  const sslrootcert = url.searchParams.get('sslrootcert');
  const useLibpqCompat = url.searchParams.get('uselibpqcompat') === 'true';
  if (useLibpqCompat && sslmode === 'verify-ca' && !sslrootcert) {
    throw new Error(
      'sslmode=verify-ca requires sslrootcert. Use verify-full or provide a CA cert path.'
    );
  }
}

Try / catch

try {
  const config = parseIntoClientConfig(connStr);
} catch (err) {
  if (/verify-ca requires specifying a CA/i.test(err.message)) {
    // Either add sslrootcert or switch to verify-full
    connStr = connStr.replace('sslmode=verify-ca', 'sslmode=verify-full');
    config = parseIntoClientConfig(connStr);
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of brianc/node-postgres@c5e8c9a57b (2026-08-03). Data as JSON: /data/errors/de1276079e381838.json. Report an issue: GitHub.