Skip to content

[Security] SCRAM-SHA-256 accepts dangerously low iteration count from server #3655

@eddieran

Description

@eddieran

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.jsparseServerFirstMessage():

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

  1. Set up a mock PostgreSQL server that responds to SCRAM-SHA-256 with r=<nonce>,s=<salt>,i=1
  2. Connect a pg client with a password
  3. The client completes the SCRAM exchange using only 1 PBKDF2 iteration
  4. 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')
}

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