Closed abonander closed 7 months ago
Hey, thanks for reporting the issue!
Timing attack in this scenario doesn't seem much practically possible, consider delta in time will be negligible. But we shouldn't neglect it either.
I'm curious as to why a lookup using Iterator::any() was chosen
Can't remember, but there doesn't seem any reason to use it, simple HashMap loopup with get()
should great.
Would you be interested in opening PR with the fix?
Thank you! ππ
Timing attack in this scenario doesn't seem much practically possible, consider delta in time will be negligible.
Timing attacks are generally statistical in nature. Even if the difference between two comparisons is a few nanoseconds, over a large number of samples that difference can be teased out.
Would you be interested in opening PR with the fix?
Unfortunately I have my own work to focus on, but this seems like a task that would be good for a first-time contributor.
Unfortunately I have my own work to focus on, but this seems like a task that would be good for a first-time contributor
No worries! Will mark this issue as good-first-issue, the only change required will be using something like:
!pairs.get(username).is_some_and(|&pass| pass == password)
Again, thanks for pointing it out π
No worries! Will mark this issue as good-first-issue, the only change required will be using something like:
Not quite, you need to use a constant time comparison function like https://crates.io/crates/constant_time_eq (note: I haven't checked whether that package is good or not, just an example).
The subtle
crate is pretty good and maintained by cryptography experts, which is why I linked to it in the OP:
use subtle::ConstantTimeEq;
!pairs.get(username).is_some_and(|pass| pass.as_bytes().ct_eq(password.as_bytes()).into())
While reading the code for how
rumqttd
handles password authentication, the following code snippet jumped out at me:https://github.com/bytebeamio/rumqtt/blob/c719181bb1e3c41a691a7827bfafd393b004e88c/rumqttd/src/link/remote.rs#L247-L249
This comparison is linear in respect to both the number of users as well as the length of both the username and password, which would theoretically allow an attacker to brute-force a login using a timing attack, sending random inputs and timing how long it takes for
rumqttd
to validate them to estimate how many characters are correct in the username and/or password.A
HashMap
lookup for the username would eliminate some of that timing information, and a constant-time comparison for the password would make a timing attack on a complete login no more efficient than brute-force.Looking closer, it appears that
pairs
in that snippet is already aHashMap
, so I'm curious as to why a lookup usingIterator::any()
was chosen.This may not be considered high-priority, especially since authentication is pluggable, but hardening against this kind of attack would mean added value for production users. I didn't see another issue about this so I just wanted to make sure you were aware of it.