password_hash: reject a PBKDF2 credential with an absurd iteration count (>10M) instead of running it — the count is read straight from the stored string, so a corrupt/hostile credential (e.g. via a compromised accounts backend) could pin a worker thread for a very long time; legitimate work factors are far below the cap
This commit is contained in:
parent
4248cc0479
commit
54c2a733fe
1 changed files with 8 additions and 0 deletions
|
|
@ -58,6 +58,11 @@ pub fn ct_eq(a: &[u8], b: &[u8]) -> bool {
|
|||
a.len() == b.len() && openssl::memcmp::eq(a, b)
|
||||
}
|
||||
|
||||
/// Upper bound on PBKDF2 iterations honoured from a stored credential. Legitimate
|
||||
/// work factors are well under this; a larger value (from a corrupt or hostile
|
||||
/// credential string) would pin a worker thread, so we reject it instead.
|
||||
const MAX_PBKDF2_ITERS: usize = 10_000_000;
|
||||
|
||||
/// PBKDF2-HMAC-SHA256 of `pass` with `salt` and `iters`, `len` bytes out.
|
||||
fn pbkdf2(pass: &str, salt: &[u8], iters: usize, len: usize) -> Option<Vec<u8>> {
|
||||
let mut out = vec![0u8; len];
|
||||
|
|
@ -86,6 +91,9 @@ pub fn verify(stored: &str, plaintext: &str) -> bool {
|
|||
if let (Ok(iters), Some(salt), Some(want)) =
|
||||
(parts[0].parse::<usize>(), unhex(parts[1]), unhex(parts[2]))
|
||||
{
|
||||
if !(1..=MAX_PBKDF2_ITERS).contains(&iters) {
|
||||
return false; // absurd/zero work factor — refuse, don't compute
|
||||
}
|
||||
if let Some(got) = pbkdf2(plaintext, &salt, iters, want.len()) {
|
||||
return ct_eq(&got, &want);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue