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
- 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.
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
- 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.
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
- Both useLibpqCompat and uselibpqcompat are set. Please use o
- Invalid ${key}: ${value}
- Invalid sslnegotiation value: "${this.sslnegotiation}". Vali
- sslnegotiation=direct requires SSL to be enabled
- SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce does not star
AI-assisted analysis of brianc/node-postgres@c5e8c9a57b (2026-08-03).
Data as JSON: /data/errors/de1276079e381838.json.
Report an issue: GitHub.