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.confmandatesscram-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.conftomd5while you patch, OR passchannel_binding=disable+ upgrade the driver. - Fast path: Use our Client-Side Sandbox below to auto-refactor your
pg_hba.confor 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_authiddump (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.