Split from #3651 as suggested by @charmander.
Summary
The SCRAM-SHA-256 implementation accepts any iteration count >= 1 from the server. A rogue PostgreSQL server can send a SCRAM challenge with i=1, making the derived key trivially cheap to brute-force offline.
Affected code
packages/pg/lib/crypto/sasl.js — parseServerFirstMessage():
const iterationText = attrPairs.get('i')
if (!iterationText) {
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration missing')
} else if (!/^[1-9][0-9]*$/.test(iterationText)) {
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: invalid iteration count')
}
const iteration = parseInt(iterationText, 10)
The regex only validates that the iteration count is a positive integer. There is no minimum bound check.
Threat model
This is a rogue server scenario. If a client connects to a malicious PostgreSQL server, the server controls the SCRAM ServerFirstMessage including the iteration count. By setting i=1, the server receives the client's SCRAM proof computed with only 1 PBKDF2 iteration. An attacker who captures this exchange can brute-force the password offline at effectively plaintext speed.
RFC 7677 (SCRAM-SHA-256) Section 4 states:
the minimum iteration count SHOULD be at least 4096
Steps to reproduce
- Set up a mock PostgreSQL server that responds to SCRAM-SHA-256 with
r=<nonce>,s=<salt>,i=1
- Connect a
pg client with a password
- The client completes the SCRAM exchange using only 1 PBKDF2 iteration
- The attacker now has a proof derived with 1 iteration — offline brute-force is trivial
Impact
Password compromise via offline brute-force after a single observed authentication exchange with a rogue server.
Suggested fix
Enforce a minimum iteration count in parseServerFirstMessage():
const iteration = parseInt(iterationText, 10)
if (iteration < 4096) {
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration count ' + iteration + ' is below minimum 4096')
}
Split from #3651 as suggested by @charmander.
Summary
The SCRAM-SHA-256 implementation accepts any iteration count >= 1 from the server. A rogue PostgreSQL server can send a SCRAM challenge with
i=1, making the derived key trivially cheap to brute-force offline.Affected code
packages/pg/lib/crypto/sasl.js—parseServerFirstMessage():The regex only validates that the iteration count is a positive integer. There is no minimum bound check.
Threat model
This is a rogue server scenario. If a client connects to a malicious PostgreSQL server, the server controls the SCRAM
ServerFirstMessageincluding the iteration count. By settingi=1, the server receives the client's SCRAM proof computed with only 1 PBKDF2 iteration. An attacker who captures this exchange can brute-force the password offline at effectively plaintext speed.RFC 7677 (SCRAM-SHA-256) Section 4 states:
Steps to reproduce
r=<nonce>,s=<salt>,i=1pgclient with a passwordImpact
Password compromise via offline brute-force after a single observed authentication exchange with a rogue server.
Suggested fix
Enforce a minimum iteration count in
parseServerFirstMessage():