Description
Hello, while reviewing the SCRAM-SHA-256 authentication flow, I noticed a potential performance and security concern that I would like to confirm.
The relevant locations appear to be:
parse_server_first() parses i= from the SCRAM server-first message as a u32, but does not appear to enforce a reasonable upper bound:
i = v.parse::<u32>().ok();
client_final() passes this value directly to hi_sha256(), where it controls the PBKDF2-HMAC-SHA256 iteration count:
for _ in 1..iters {
u = hmac_sha256(password, &u);
for (o, ui) in out.iter_mut().zip(u.iter()) {
*o ^= *ui;
}
}
The computation runs after reading the server-first message and before reaching the next .await, so the complete loop executes synchronously on the current Tokio worker without yielding.
Potential impact
The normal PostgreSQL default is i=4096, for which the impact should be minimal.
However, a malicious, impersonated, compromised, or severely misconfigured PostgreSQL server could return a valid SCRAM message containing an excessively large i= value. This may cause the client to perform a large amount of server-controlled synchronous CPU work and delay other tasks running on the same Tokio runtime.
In a local single-threaded Tokio test using the current ScramClient::client_final() implementation, i=500000 caused approximately 78 ms of scheduling delay. The execution time grew roughly linearly with i, while the current parsing logic can theoretically accept values up to u32::MAX.
I may have missed a constraint applied elsewhere in the project. If no additional constraint exists, this could potentially allow client-side computational amplification or denial of service.
Possible mitigation
It may be worth enforcing a reasonable supported range for the SCRAM iteration count and rejecting unusually large values. For example:
const MAX_SCRAM_ITERATIONS: u32 = 100_000;
if !(1..=MAX_SCRAM_ITERATIONS).contains(&iters) {
return Err(PgWireError::Auth(
"SCRAM iteration count is outside the supported range".into(),
));
}
The exact limit may need to be chosen based on PostgreSQL compatibility and the requirements of this project. For accepted values that may still be expensive, spawn_blocking could also help avoid keeping a Tokio worker busy.
Please let me know if I have misunderstood the call flow or the potential impact.
Thank you for taking the time to review this.
Description
Hello, while reviewing the SCRAM-SHA-256 authentication flow, I noticed a potential performance and security concern that I would like to confirm.
The relevant locations appear to be:
ScramClient::parse_server_first()parses the iteration count supplied by the server.hi_sha256()performs the iteration-controlled HMAC-SHA256 loop.WorkerState::auth_scram()callsScramClient::client_final()synchronously between two.awaitpoints.parse_server_first()parsesi=from the SCRAM server-first message as au32, but does not appear to enforce a reasonable upper bound:client_final()passes this value directly tohi_sha256(), where it controls the PBKDF2-HMAC-SHA256 iteration count:The computation runs after reading the server-first message and before reaching the next
.await, so the complete loop executes synchronously on the current Tokio worker without yielding.Potential impact
The normal PostgreSQL default is
i=4096, for which the impact should be minimal.However, a malicious, impersonated, compromised, or severely misconfigured PostgreSQL server could return a valid SCRAM message containing an excessively large
i=value. This may cause the client to perform a large amount of server-controlled synchronous CPU work and delay other tasks running on the same Tokio runtime.In a local single-threaded Tokio test using the current
ScramClient::client_final()implementation,i=500000caused approximately 78 ms of scheduling delay. The execution time grew roughly linearly withi, while the current parsing logic can theoretically accept values up tou32::MAX.I may have missed a constraint applied elsewhere in the project. If no additional constraint exists, this could potentially allow client-side computational amplification or denial of service.
Possible mitigation
It may be worth enforcing a reasonable supported range for the SCRAM iteration count and rejecting unusually large values. For example:
The exact limit may need to be chosen based on PostgreSQL compatibility and the requirements of this project. For accepted values that may still be expensive,
spawn_blockingcould also help avoid keeping a Tokio worker busy.Please let me know if I have misunderstood the call flow or the potential impact.
Thank you for taking the time to review this.