highCVE-2026-54291Jul 21, 2026

CVE-2026-54291: PostgreSQL JDBC Driver Silent SCRAM Channel-Binding Downgrade

Shubham Kandhare
Security Engagement Manager, SecureLayer7

A network attacker who can intercept TLS traffic can silently strip SCRAM channel-binding protection from a PostgreSQL JDBC connection that explicitly requires it, by presenting a server certificate…

Packageorg.postgresql:postgresql
Ecosystemmaven
Affected>= 42.7.4, < 42.7.12
Fixed in42.7.12

The problem

pgJDBC 42.7.4 through 42.7.11 does not enforce channelBinding=require after SCRAM mechanism negotiation. ScramAuthenticator checks only that the server advertised a -PLUS mechanism, but never verifies the mechanism that was actually selected, and never rejects an empty channel-binding value.

The bundled scram-client 3.1/3.2 compounds the problem: when the server certificate uses Ed25519, Ed448, or a post-quantum algorithm, TlsServerEndpoint cannot derive the tls-server-end-point hash, swallows the NoSuchAlgorithmException, and returns an empty byte array instead of failing.

The two gaps together let a MITM silently downgrade the session from SCRAM-SHA-256-PLUS to plain SCRAM-SHA-256 with no client-side error.

Proof of concept

A working proof-of-concept for CVE-2026-54291 in org.postgresql:postgresql, with the exact payload below.

bash
# Attacker-controlled PostgreSQL server presents an Ed25519 TLS certificate.
# The vulnerable JDBC client (42.7.4-42.7.11) silently accepts plain SCRAM-SHA-256
# even though it explicitly requested channelBinding=require.

# 1. Generate an Ed25519 server certificate (no tls-server-end-point hash exists for this algorithm)
openssl genpkey -algorithm Ed25519 -out server.key
openssl req -new -x509 -key server.key -out server.crt -days 365 \
  -subj "/CN=db.example.com"

# 2. Configure PostgreSQL to use this certificate (postgresql.conf):
#    ssl_cert_file = 'server.crt'
#    ssl_key_file  = 'server.key'

# 3. Victim JDBC client connects with what it believes is a channel-binding-enforced session:
# jdbc:postgresql://db.example.com:5432/mydb?channelBinding=require&ssl=true
#
# scram-client returns empty byte[] for the binding hash -> ScramAuthenticator
# does not detect this and does not check that the negotiated mechanism ends in -PLUS.
# Authentication completes over plain SCRAM-SHA-256; MITM wins.

Two missing checks in ScramAuthenticator are the root cause (CWE-636, CWE-757). First, after scram-client returns the channel-binding value, the driver never tests whether the byte array is empty and does not abort the handshake. Second, after SCRAM mechanism negotiation completes, the driver never asserts that the selected mechanism name ends in -PLUS when channelBinding=require is configured.

The patch (commit 77df98e) adds both guards directly in pgJDBC's own code, independent of the scram-client version: it fails the connection immediately when the extracted binding data is empty (naming the unsupported certificate algorithm in the error), and it rejects the connection post-negotiation if the chosen mechanism is not a -PLUS variant. scram-client 3.3 separately fixes the library side by propagating the exception instead of swallowing it.

The fix

Upgrade to org.postgresql:postgresql 42.7.12 or later. No configuration workaround exists for the affected releases. As an interim network-layer mitigation only, set sslmode=verify-full with a truststore containing only your server's CA so that a MITM cannot substitute a certificate.

Connections relying solely on channelBinding=require for MITM protection must upgrade.

Reported by KEIJOT.

References: [1][2][3][4][5]

Related research