9

I've read that doing a string comparison is not the preferred way to determine if hmac signatures match. (Go to Step 5) So, in Node, given something like this

const hmac = crypto.createHmac("sha256", signingSecret).update(buf, encoding);
const computed = `${version}=${hmac.digest('hex')}`;

if(computed !== req.header("signature")){
   throw
}

If not for string comparison, what is the preferred way of doing this line: computed !== req.header("signature")?

Newtang
  • 6,414
  • 10
  • 49
  • 70
  • More general info: https://stackoverflow.com/questions/31095905/whats-the-difference-between-a-secure-compare-and-a-simple – dskrvk Oct 03 '19 at 19:24

1 Answers1

24

The reason why an ordinary string comparison is not preferred is that it will typically return a result as soon as it finds a difference between the two strings. This can open the door to a timing-based attack; if I'm trying to guess the correct HMAC for a message and my guess differs from the real string in its first character, I'll get a "fail" response more quickly than if the difference is in the second, third, fourth ... character. Yes, the time difference is tiny, but it really can be measurable.

The preferred technique is to use what's called a "constant-time comparison" function. This kind of function is designed to consume the same amount of time regardless of the position where the arguments differ. By not returning as soon as it finds a mismatch, it gives an attacker no information about the position of the mismatch.

In Node the appropriate function is crypto.timingSafeEqual(a,b).

ottomeister
  • 5,415
  • 2
  • 23
  • 27