Initializing Enclave...

How to Fix PostgreSQL 'Invalid Authentication Method scram-sha-256' Connection Error

Threat/Impact Level: HIGH | Downtime Risk: HIGH | Time to Fix: 5–15 mins


TL;DR

  • What broke: Your PostgreSQL client or libpq build predates SCRAM-SHA-256 (introduced in PostgreSQL 10 / libpq 10). The server's pg_hba.conf mandates scram-sha-256, your client can't negotiate it, connection dies immediately.
  • How to fix it: Upgrade libpq/driver to ≥10, OR temporarily downgrade pg_hba.conf to md5 while you patch, OR pass channel_binding=disable + upgrade the driver.
  • Fast path: Use our Client-Side Sandbox below to auto-refactor your pg_hba.conf or connection string without leaking credentials.

The Incident (What Does the Error Mean?)

Raw error output from psql, application logs, or JDBC:

FATAL: could not connect to server: Invalid authentication method 'scram-sha-256' for user "app_user"
# or in JDBC:
org.postgresql.util.PSQLException: FATAL: password authentication failed for user "app_user"
Detail: Client authentication method 'scram-sha-256' is not supported.

Immediate consequence: Every connection attempt from this client fails at the authentication handshake. No queries execute. If this is a connection pool (PgBouncer, HikariCP, pgpool-II), the entire pool goes offline. Production traffic hits a wall.

The server sent a SCRAM-SHA-256 challenge as dictated by pg_hba.conf. The client's libpq (or JDBC driver, asyncpg, psycopg2 build) responded that it does not implement that SASL mechanism. PostgreSQL server-side logs show:

LOG: connection received: host=10.0.1.45 port=52310
FATAL: unsupported authentication method requested by client

The Attack Vector / Blast Radius

This is not just a nuisance misconfiguration — the pressure to "just switch to md5" to restore service is the actual security risk.

MD5 password hashing in PostgreSQL is cryptographically broken. MD5-hashed passwords in pg_data/global/pg_authid are trivially crackable offline if an attacker achieves read access to the data directory, a backup, or a WAL stream. SCRAM-SHA-256 was mandated precisely to replace MD5.

Blast radius if you revert to MD5 to fix the outage:

  • Any leaked pg_authid dump (RDS snapshot misconfiguration, S3 backup exposure, compromised replica) exposes all user passwords via offline rainbow table or hashcat attack in minutes.
  • Compliance frameworks (SOC2, PCI-DSS, HIPAA) explicitly flag MD5 PostgreSQL auth as a finding.
  • If your RDS/Aurora/Cloud SQL instance is shared-tenancy adjacent, this widens the lateral movement surface.

The real fix is upgrading the client, not downgrading the server.


How to Fix It (The Solution)

Root Cause Checklist — Run These First

# Check libpq version on the client machine
psql --version
# Need: psql (PostgreSQL) 10.x or higher

# Check what auth method the server is advertising
psql -h yourhost -U app_user -d yourdb 2>&1 | grep -i scram

# Check pg_hba.conf on the server
grep -E 'scram|md5|trust' /etc/postgresql/15/main/pg_hba.conf

Fix 1 — Upgrade the Client Driver (Correct Fix)

Python / psycopg2:

- psycopg2==2.7.3   # libpq 9.x bundled, no SCRAM support
+ psycopg2-binary==2.9.9  # libpq 15 bundled, full SCRAM-SHA-256

Node.js / pg:

- "pg": "^7.18.2"   # No SCRAM support
+ "pg": "^8.11.3"   # Full SCRAM-SHA-256 via libpq 10+

Java / JDBC:

- <dependency>
-   <groupId>org.postgresql</groupId>
-   <artifactId>postgresql</artifactId>
-   <version>9.4.1212</version>
- </dependency>
+ <dependency>
+   <groupId>org.postgresql</groupId>
+   <artifactId>postgresql</artifactId>
+   <version>42.7.3</version>
+ </dependency>

Fix 2 — pg_hba.conf Temporary Mitigation (NOT for production long-term)

Only use this if you are mid-incident and need to restore service while patching clients.

# /etc/postgresql/15/main/pg_hba.conf
- host    all    app_user    10.0.0.0/8    scram-sha-256
+ host    all    app_user    10.0.0.0/8    md5

Then reload (no restart needed):

systemctl reload postgresql
# or in psql as superuser:
SELECT pg_reload_conf();

⚠ Schedule the driver upgrade and revert to scram-sha-256 within the same sprint. Track this as a security debt ticket.


Fix 3 — Re-encrypt the Password as SCRAM After Driver Upgrade

Even after upgrading the driver, if the password was originally stored as MD5 in pg_authid, the server may still fall back. Force re-encryption:

-- Run as superuser after setting scram-sha-256 in pg_hba.conf
SET password_encryption = 'scram-sha-256';
ALTER USER app_user WITH PASSWORD 'your_strong_password';

-- Verify:
SELECT rolname, rolpassword FROM pg_authid WHERE rolname = 'app_user';
-- rolpassword should start with: SCRAM-SHA-256$...

Enterprise Best Practice — Enforce SCRAM Cluster-Wide

# postgresql.conf
- password_encryption = md5
+ password_encryption = scram-sha-256

# pg_hba.conf — lock down ALL host entries
- host    all    all    0.0.0.0/0    md5
+ host    all    all    0.0.0.0/0    scram-sha-256
# local unix socket can remain trust or peer for superuser ops
  local   all    postgres             peer

For RDS / Aurora, set via parameter group:

- rds.force_ssl = 0
- password_encryption = md5
+ rds.force_ssl = 1
+ password_encryption = scram-sha-256

💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.


Prevention in CI/CD

1. Checkov — Scan IaC for MD5 Auth

If you're provisioning PostgreSQL via Terraform (RDS, Cloud SQL, self-managed):

checkov -d . --check CKV_AWS_129  # Enforces encrypted connections
# Add custom check for password_encryption parameter

2. OPA / Conftest Policy — Block md5 in pg_hba.conf

# policy/pg_hba_scram.rego
package postgresql

deny[msg] {
  line := input.pg_hba[_]
  line.method == "md5"
  msg := sprintf("pg_hba.conf line '%v' uses deprecated md5 auth. Require scram-sha-256.", [line])
}
conftest test pg_hba.conf --policy policy/

3. Pin Driver Versions in CI with Dependabot / Renovate

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "weekly"
    allow:
      - dependency-name: "psycopg2-binary"

4. Pre-commit Hook — Grep for md5 in Config Files

#!/bin/bash
# .git/hooks/pre-commit
if grep -rE 'password_encryption\s*=\s*md5' .; then
  echo "ERROR: md5 password_encryption detected. Use scram-sha-256."
  exit 1
fi

5. Integration Test — Assert SCRAM in CI Pipeline

# In your pipeline, after spinning up the test DB container:
PGPASSWORD=$TEST_PW psql -h localhost -U testuser -d testdb \
  -c "SELECT rolpassword FROM pg_authid WHERE rolname='testuser';" \
  | grep -q 'SCRAM-SHA-256' || (echo 'FAIL: password not SCRAM-encoded' && exit 1)

Summary: Upgrade the client driver. Re-encode passwords as SCRAM. Lock pg_hba.conf to scram-sha-256. Gate this in CI so a dependency downgrade never silently reintroduces MD5 auth in your fleet.

Related Diagnostics

"Part of the Security Utility Matrix."

View all 140 Security Tools →