Closed robrwo closed 3 years ago
Note running tests on different machines (different Linux versions) and different versions of Perl, the timings are less consistent, but generally diff1 takes less time than diff5 or diff9 or same.
Actually, this is bitwise or so it shouldn't short-circuit. Closing this as invalid, although I suspect there's something else going on that leads this to leak information about where the difference is.
Running it locally here I don't get a meaningful difference. I wonder if you happened to have a lucky run. (By this I mean my results weren't ordered at all).
I've run it many times on numerous machines, Perl 5.22.2, 5.28.1 and 5.30.3. I've found the following: diff1 is consistently less time than all others. Anyhow, I closed this when I realised that the ordering was not as consistent as it first appeared.
Also, because it immediately returns when the two strings do not have equal length, then it is possible to guess how long the string is. (Keep adding a character to string, and time the compare, then look for the comparison that took the most time.)
If the lengths are unequal, you could pad them and run them through the bitwise checker, problem is then unequal length strings will take more time rather than less.
The purpose of the function is to avoid leaking information about a secret string to an attacker who is using the time it takes for the method to run.
Because the function exits quickly when the string lengths are unequal, it makes it easy to for the attacker to guess how long the secret string is.
If you appended a random length string to both values the problem would be mitigated, no?
Isn't there a standard implementation for the function we can steal?
I don't know who this is but a quick search found this and it seems sane: https://github.com/vadimdemedes/secure-compare/blob/master/index.js
diff --git a/lib/Mojo/Util.pm b/lib/Mojo/Util.pm
index 630607490..4ee82b5e8 100644
--- a/lib/Mojo/Util.pm
+++ b/lib/Mojo/Util.pm
@@ -276,8 +276,8 @@ sub scope_guard { Mojo::Util::_Guard->new(cb => shift) }
sub secure_compare {
my ($one, $two) = @_;
- return undef if length $one != length $two;
- my $r = 0;
+ my $r = length $one != length $two;
+ $two = $one if $r;
$r |= ord(substr $one, $_) ^ ord(substr $two, $_) for 0 .. length($one) - 1;
return $r == 0;
}
That looks alright. In the POD you should not that the "secret" should be the second argument.
I'll create a PR if you'd like.
Steps to reproduce the behavior
I would expect the cumulative or code to short-circuit comparisons once r is true.
I wrote a rudimentary benchmark:
It seems that the latter a difference occurs in a string, the longer the comparison takes:
So this does leak information about the strings.
Expected behavior
There should be no difference in the timing.
Actual behavior
The latter a difference occurs in a string, the longer the check takes.