TL;DR;
In couple of open-source libraries, in various contexts, people don't use constant time comparision functions to verify signatures, this can lead to timing attacks. So please use hmac.compare_digest, or other function in your language.
Problem
I was integrating with third-party payment service. When payment is processed they issue POST request to our shop, this POST is signed using following algorithm:
- They send signature in header;
- To compute signature I need to concatenate POST body with a secret and compute hash function on it;
- I verify both signatures are equal.
Nothing unsafe and unexpected (we'll I'd probably use HMAC instead of concatenation --- just for sake using well designed primitives).
There is open source library for this payment processor (I didn't use it due to weird design choices) which implements this checking as follows:
def verify(signature, content, secret_key):
actual_signature = compute_signature(content, secret_key)
# Unsafe! Don't use.
if actual_signature != signature:
return "ERROR"
return "OK"
This construction is quite unsafe.
Timing attacks
Let's assume attacker can accurately measure execution time of said notification handler.
Now let's assume that expected signature for a payload is abc, now attacker sends the same payload with signatures aaa, baa, caa.
Now time it takes for your program to check if actual_signature != signature will be slightly longer for aaa than for baa --- it works that way because == operator checks strings letter by letter and returns on first mismatch operator for strings is kinda sorta similar to:
def strings_equal(left, right):
if len(left) != len(right):
return False
for ii in range(len(left)):
if left[ii] != right[ii]:
return False
return True
Now usually you want your comparision code to exit as early as possible, but not in case of comparing signatures.
So now attacker sees that it takes longer to reject payload signed by aaa than baa. He then can guess that first letter of signature is indeed a (he then can repeat the same scenario sending aaa and aba and aca...).
Constant time compare
In case of comparing signatures you usually should use some version of constant time compare function --- that is a comparision function deliberately designed to always take te same time.
In python there is handy function hmac.compare_digest so just use library one.
If you wonder how to implement one here is a rough example:
def strings_equal(left, right):
if len(left) != len(right):
return False
strings_are_different = False
for ii in range(len(left)):
strings_are_different = strings_are_different or left[ii] != right[ii]:
return not strings_are_different
Here are some points:
- We leak timing information that allow attacker to recover signature length, this is not a problem usually, as signature length can be obtained by reading protocol documentation ;)
- I'm not sure is CPython VM wouldn't leak timing information from this implementation (as python comparision is very complex), nevertheless just stick to standard hmac.compare_digest.
Practicality of timing attacks
In practice attacker can't accurately measure time for invocation of your HTTP request handlers --- he can measure time for full packet roundtrip, which includes variable connection latencies, and a lot of other noise.
He can however repeat each request couple thousand of times and average timings to reduce noise (if you throttle your API you can alleviate this a bit).
So while timing attacks are not easy, they are practically feasible.