Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
635d5db736 fix(sql): enforce minimum PBKDF2 iteration count in SCRAM-SHA-256 auth
Reject SCRAM SASLContinue messages with PBKDF2 iteration counts below
4096 (the RFC 7677 recommended minimum). Without this check, a MITM
attacker could modify the server's iteration count to 1, reducing
PBKDF2 to a single HMAC iteration and making offline password
brute-force ~4096x faster.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-12 04:43:43 +00:00
2 changed files with 12 additions and 0 deletions

View File

@@ -20,6 +20,7 @@ pub const AnyPostgresError = error{
OutOfMemory,
Overflow,
PBKDFD2,
SASL_ITERATION_COUNT_TOO_LOW,
SASL_SIGNATURE_MISMATCH,
SASL_SIGNATURE_INVALID_BASE64,
ShortRead,
@@ -97,6 +98,7 @@ pub fn postgresErrorToJS(globalObject: *jsc.JSGlobalObject, message: ?[]const u8
error.NullsInArrayNotSupportedYet => "ERR_POSTGRES_NULLS_IN_ARRAY_NOT_SUPPORTED_YET",
error.Overflow => "ERR_POSTGRES_OVERFLOW",
error.PBKDFD2 => "ERR_POSTGRES_AUTHENTICATION_FAILED_PBKDF2",
error.SASL_ITERATION_COUNT_TOO_LOW => "ERR_POSTGRES_SASL_ITERATION_COUNT_TOO_LOW",
error.SASL_SIGNATURE_MISMATCH => "ERR_POSTGRES_SASL_SIGNATURE_MISMATCH",
error.SASL_SIGNATURE_INVALID_BASE64 => "ERR_POSTGRES_SASL_SIGNATURE_INVALID_BASE64",
error.TLSNotAvailable => "ERR_POSTGRES_TLS_NOT_AVAILABLE",

View File

@@ -8,6 +8,13 @@ const server_signature_base64_len = bun.base64.encodeLenFromSize(server_signatur
const salted_password_byte_len = 32;
/// Minimum PBKDF2 iteration count to prevent MITM downgrade attacks.
/// RFC 7677 recommends a minimum of 4096 iterations for SCRAM-SHA-256.
/// A MITM attacker could modify the SASLContinue message to set a low
/// iteration count (e.g., i=1), making offline password brute-force
/// significantly faster.
const min_iteration_count = 4096;
nonce_base64_bytes: [nonce_base64_len]u8 = .{0} ** nonce_base64_len,
nonce_len: u8 = 0,
@@ -35,6 +42,9 @@ fn hmac(password: []const u8, data: []const u8) ?[32]u8 {
}
pub fn computeSaltedPassword(this: *SASL, salt_bytes: []const u8, iteration_count: u32, connection: *PostgresSQLConnection) !void {
if (iteration_count < min_iteration_count) {
return error.SASL_ITERATION_COUNT_TOO_LOW;
}
this.salted_password_created = true;
if (Crypto.EVP.pbkdf2(&this.salted_password_bytes, connection.password, salt_bytes, iteration_count, .sha256) == null) {
return error.PBKDFD2;