Skip to content

Client treats a ReadyForQuery before any authentication message as a connected session #3841

Open

Description

@cayter

Versions: pg 8.23.1, pg-protocol 1.16.1, Node.js 26.13.0. Compared with libpq from PostgreSQL 19rc1.

What happens: during startup, Client takes the first ReadyForQuery as a finished connection, whatever came before it. A server that answers the startup message with ReadyForQuery alone, sending no authentication message, connects. The client has a password, the server never asks for it, and connect() resolves. libpq refuses the same server.

Reproduction (no PostgreSQL needed: a scripted server stands in for one):

import net from 'node:net'
import pg from 'pg'

// A server that answers the startup message with ReadyForQuery, sending no authentication message
const message = (type, body) => {
  const head = Buffer.alloc(5)
  head.write(type, 0)
  head.writeInt32BE(body.length + 4, 1)
  return Buffer.concat([head, body])
}
const server = net.createServer((socket) => {
  socket.once('data', () => socket.write(message('Z', Buffer.from('I'))))
})
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve))

const client = new pg.Client({
  host: '127.0.0.1',
  port: server.address().port,
  user: 'app',
  password: 'secret',
  database: 'app',
})
try {
  await client.connect()
  console.log('connected')
} catch (error) {
  console.log('refused:', error.message)
}
client.end().catch(() => {})
server.close()

Output:

connected

libpq against the same server (psql "host=127.0.0.1 port=<port> user=app dbname=app sslmode=disable", with PGPASSWORD=secret):

psql: error: connection to server at "127.0.0.1", port <port> failed: expected authentication request from server, but received Z

Where, in the installed sources:

  • pg/lib/client.js:261 hands every ReadyForQuery to _handleReadyForQuery.
  • pg/lib/client.js:373-385 (_handleReadyForQuery): while _connecting, it marks the client connected and calls the connect callback, with no check that authentication happened.
  • The client has no authenticationOk listener (:250-261), so nothing records that the server finished authenticating.

Expected: as libpq does, a ReadyForQuery (or any message other than an authentication request, an ErrorResponse or a NegotiateProtocolVersion) before AuthenticationOk ends the connection attempt with an error, for example "expected authentication request from server, but received ReadyForQuery".

Impact: a server can already skip authentication by sending AuthenticationOk, so this is protocol conformance rather than a way in by itself. It matters for code that enforces libpq's require_auth or channel_binding=require on top of pg: it can check the authentication messages it sees, but cannot tell that none came, short of overriding the private _handleReadyForQuery. pgx recently fixed the related bypass on its side (GHSA-95fj-3fxw-g62g).

Related: #1508 ("Unknown authenticationOk message type") is about parsing that message, not about requiring it. I found no issue about this one.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions