The difference is that a secure comparison between a secret and a test string does not reveal information about the secret in the time it takes to run.
For instance, suppose the secret was aaaaaaaaab
and you compare with the test strings b
, bbbbbbbbbb
and aaaaaaaaaa
.
A classical comparison yields false immediately for b
because the lengths do not match.
For bbbbbbbbbb
is false almost immediately because the first character does not match.
For aaaaaaaaaa
is also false, but not so immediately (takes more microseconds or nanoseconds).
So, the longer it takes, the closer your test string is to the secret.
An attacker can for instance try to guess the length by sending many tests of different lengths and finding the smallest.
Then, he can proceed to find the first character in a similar fashion, and so on, until finding out the secret.
To avoid this, as recommended by @WDS, you can do a simple for loop instead:
// (Javascript)
function equals(test, secret) {
// String comparison that does not leak timing information
let diffs = test.length !== secret.length ? 1 : 0;
for (let i = 0; i < test.length; i++) if (test[i] !== secret[i]) diffs++;
return diffs == 0;
}
The accepted answer points to Node's timingSafeEqual(a, b)
which is mostly designed for hashed inputs since, as said in the comments, it expects the inputs to have the same lengths.
Using this function without hashing can lead you to troubles since you can not just return false
when the input lengths mismatch because this would leak information about the length of the secret.