2

I've seen code like this:

 if(password.length !== allowedPassword.length || !crypto.timingSafeEqual(password, allowedPassword))

So timingSafeEqual is supposed to use the same amount of time to compare 2 passwords, in order to prevent the attack to estimate the complexity of the password.

My question is, if the both passwords are not equal in length, the comparison will never run, so how does this help at all? If the comparison runs, then the attack knows the exact length of the password because the time is always the same, and if it doesn't run he can estimate the complexity lol

Alex
  • 527
  • 4
  • 7

3 Answers3

2

I've written before that revealing the length of your password is not really that big a deal. See my answer here.

What does a timingSafeEqual do? Well, most language's string equality behaves like this:

bool equal(strA, strB) {
  assert(len(strA) == len(strB));
  for (i < len(strA)) {
    if (strA[i] != strB[i]) return false;
  }
  return true;
}

That means that it will start from the beginning of the strings and "fail out" when it finds the first character that doesn't match. For example, equal("aaa", "bbb") will fail out on the first loop while equal("aaa","abb") will fail out on the second loop. A careful attacker can detect this timing difference and brute-force your password one character at a time.

On the other hand, a timingSafeEqual will always do exactly the same number of operations regardless of what it finds, for example it would be closer to:

bool timingSafeEqual(strA, strB) {
  assert(len(strA) == len(strB));
  bool match = true;
  for (i < len(strA)) {
    match ^= strA[i] == strB[i];
  }
  return match;
}

(you need to be a bit careful when writing the assignment line to make sure your compiler doesn't get clever and remove no-op assignments, cause then you're back to having a timing difference.)

Final note, if you're doing things properly and comparing two salted password hashes then it doesn't really matter whether you use a timing-invariant comparison or not because an attacker learning your password hash doesn't really help them.

Mike Ounsworth
  • 57,707
  • 21
  • 150
  • 207
  • Pretty sure you meant to use `&=` rather than `^=`... using xor-equals would mean "aaa" and "aab" would return equal, or indeed "a" and "z", but not "123" and "123", since the state of `match` would toggle each time the two characters being compared were the same! – CBHacking Aug 02 '21 at 07:31
  • Also, learning a password hash is useful (not *as* useful as a raw password, but more than nothing; throw it in JtR). The reason hashed passwords aren't vulnerable to this kind of timing attack is because the attacker can't vary one character at a time in the hash digest while holding everything before it constant. – CBHacking Aug 02 '21 at 07:35
1

Yes. That code is wrong.

The test should only use the timed function:

if(!crypto.timingSafeEqual(password, allowedPassword))

Clearly some people do not understand the code they write.

But you'd only be able to determine the length of the password. Hopefully, your's pretty long... He! He!

Alexis Wilke
  • 862
  • 5
  • 19
  • But the length check is required because the function requires that both arguments have the same length – Alex Aug 15 '20 at 09:48
  • Hmm... then maybe you have to pad the passwords so they all are a certain length (i.e. the max. that you allow). Pad with spaces or something like that, until the designers of that function fix their terrible mistake. – Alexis Wilke Aug 15 '20 at 09:55
0

timing attack needs the complexity of each character in the generated string (hash of a password). so instead of having your function 32 different timing reading for an MD5 string, for example, using the above code will generate only 1 time, as password.length == allowedPassword.length will always be true.

if you are using a hashing that produces a different string length, then your code will only have 2 different times.

1- time with matched length + timingSafeEqual

2- time without matched length.

this is not sufficient to guess the rest of the hash, but it still reveals the pw length.

some might use this info to know it is a date or phone number, but it is hard to get more info beyond that.

an Example is here

Shady Keshk
  • 101
  • 1