{"id":"94e791288ec562f1","repo":"jackc/pgx","slug":"bad-authentication-message-size","errorCode":null,"errorMessage":"bad authentication message size","messagePattern":"bad authentication message size","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pgproto3/authentication_cleartext_password.go","lineNumber":24,"sourceCode":"\t\"errors\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\n// AuthenticationCleartextPassword is a message sent from the backend indicating that a clear-text password is required.\ntype AuthenticationCleartextPassword struct{}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*AuthenticationCleartextPassword) Backend() {}\n\n// Backend identifies this message as an authentication response.\nfunc (*AuthenticationCleartextPassword) AuthenticationResponse() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *AuthenticationCleartextPassword) Decode(src []byte) error {\n\tif len(src) != 4 {\n\t\treturn errors.New(\"bad authentication message size\")\n\t}\n\n\tauthType := binary.BigEndian.Uint32(src)\n\n\tif authType != AuthTypeCleartextPassword {\n\t\treturn errors.New(\"bad auth type\")\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *AuthenticationCleartextPassword) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'R')\n\tdst = pgio.AppendUint32(dst, AuthTypeCleartextPassword)\n\treturn finishMessage(dst, sp)\n}\n","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/jackc/pgx/blob/ec1a0befd22592cffffdeeb0a50311b506372f4c/pgproto3/authentication_cleartext_password.go#L6-L42","documentation":"Returned by AuthenticationCleartextPassword.Decode in pgproto3/authentication_cleartext_password.go:24 when the body of an 'R' (Authentication) message is not exactly 4 bytes. This message carries only the 4-byte auth-type code (AuthTypeCleartextPassword = 3) and nothing else, so any other length is a protocol violation. In the normal client path it is essentially unreachable because the frontend reads exactly bodyLen bytes and dispatch is driven by that same 4-byte code; it surfaces when the bytes are corrupted or when Decode is invoked directly on untrusted data (proxies, fuzzing, mock servers).","triggerScenarios":"A server or intermediary sends an AuthenticationCleartextPassword 'R' message whose declared length is not 8 total (4 length + 4 type). Causes: a truncated/mangled TCP stream, a TLS-terminating proxy corrupting the frame, a non-PostgreSQL service (HTTP health check, wrong-port service) replying with arbitrary bytes that happen to parse as 'R', or direct pgproto3.Decode usage on hand-built bytes.","commonSituations":"Connecting to the wrong port (e.g. an HTTP/Redis server) whose greeting bytes align with 'R'; a misconfigured PgBouncer/HAProxy mangling the startup handshake; packet corruption on a flaky link; calling pgproto3 in a custom proxy/mock-server on attacker-controlled input.","solutions":["Verify the host:port actually points at a PostgreSQL server (psql connects cleanly).","If behind a proxy/pooler, bypass it temporarily to confirm the handshake; check the proxy logs for frame corruption.","Enable TLS or fix the broken TLS terminator so the auth frame cannot be corrupted in flight.","When using pgproto3 directly, validate message length against the protocol before calling Decode."],"exampleFix":"// direct pgproto3 usage: validate before decode\nif len(body) != 4 {\n    return fmt.Errorf(\"rejecting malformed cleartext auth frame: len=%d\", len(body))\n}\nvar m pgproto3.AuthenticationCleartextPassword\nif err := m.Decode(body); err != nil { return err }","handlingStrategy":"try-catch","validationCode":"// Before connecting, sanity-check that the endpoint speaks PostgreSQL.\n// psql -h host -p port -U user -d db  should succeed; or probe with a tiny net.Dial.\nconn, err := pgconn.Connect(ctx, connString)\nif err != nil { /* err wraps \"bad authentication message size\" */ }","typeGuard":null,"tryCatchPattern":"conn, err := pgconn.Connect(ctx, connString)\nif err != nil {\n    if strings.Contains(err.Error(), \"bad authentication message size\") {\n        // endpoint is not a well-behaved PostgreSQL server / frame was corrupted\n        return fmt.Errorf(\"invalid PostgreSQL handshake at %s: %w\", connString, err)\n    }\n    return err\n}","preventionTips":["Point the connection string at a verified PostgreSQL host:port.","Prefer TLS (sslmode) to prevent in-flight frame corruption.","Bypass or fix proxies/poolers that mangle the startup handshake.","When using pgproto3 directly, assert len(body)==4 before AuthenticationCleartextPassword.Decode."],"tags":["authentication","protocol","cleartext-password","pgproto3","connection"],"analyzedSha":"ec1a0befd22592cffffdeeb0a50311b506372f4c","analyzedAt":"2026-08-04T22:52:11.263Z","schemaVersion":2}