wahern / luaossl

Most comprehensive OpenSSL module in the Lua universe.

Home Page:http://25thandclement.com/~william/projects/luaossl.html

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add support for Timing attack safe string comparison

olueiro opened this issue · comments

Hi!

Is it possible to implement a PHP's hash_equals similar function?

Modern cryptography protocols are more likely to be broken by side-channel information leaks than the persistent efforts of clever mathematicians.

One of the common cryptographic side-channels that developers should be aware of is how long a specific operation, such as a string comparison, takes to complete. Thus, they are called timing attacks.

A common place to find a timing attack vulnerability in a cryptography library is in the MAC validation logic.

if ($mac === hash_hmac('sha256', $message, $authKey)) {
    return decrypt($message);
}

Timing attacks are possible because string comparison (usually implemented internally via memcmp()) is optimized. For example: If you compare "apple" with "acorn", it will take measurably longer (on a nanosecond scale) than if you were to compare "apple" with "pecan", simply because the first character matches. This allows an attacker to slowly guess the first character, then the second, etc. of a valid Message Authentication Code for a forged message.

Source

Short lua strings are interned: this isn't something that luaossl can really address.

Furthermore, luaossl provides keys as objects (not strings), so unless you're stringifying your keys, this isn't a problem,

A common place to find a timing attack vulnerability in a cryptography library is in the MAC validation logic.

Are hmacs vulnerable to this? Could you provide a link that says that a non-constant-time comparison of the HMAC results in leaking key information?

Thanks for reply :)
I'll implement this (in Lua) in my future Lapis website. Scroll into gist comments, timing attacks are mentioned.

Timing attacks are not a widely recognized problem, since they require significant skill and resources of the attacker. To prevent these attacks constant-time string comparisons can be used. These implementations are rather hard to get right, without leaking timing information (average HTTP request time?)

Are hmacs vulnerable to this? Could you provide a link that says that a non-constant-time comparison of the HMAC results in leaking key information?

luaossl already uses lua_pushlstring rather than lua_pushstring, so "long strings" will be preferred where lua supports them (AFAIK, this does not include LuaJIT as used by lapis). The instant that you get a hmac result as a string, there is a potential timing attack; even if you don't compare it. This suggests that perhaps a hmac:verify(different_hmac_object) API might be useful.
But before we even get that far: you still need to have the key as a string to pass to hmac.new in the first place! This can be a timing vulnerability in lapis for any strings created (e.g. from json fields!).

Thanks for that answer, it was helpful to get my thoughts onto the right process.

Real life sample (from Telegram official docs)

<?php
define('BOT_TOKEN', 'XXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXX'); // place bot token of your bot here
function checkTelegramAuthorization($auth_data) {
  $check_hash = $auth_data['hash'];
  unset($auth_data['hash']);
  $data_check_arr = [];
  foreach ($auth_data as $key => $value) {
    $data_check_arr[] = $key . '=' . $value;
  }
  sort($data_check_arr);
  $data_check_string = implode("\n", $data_check_arr);
  $secret_key = hash('sha256', BOT_TOKEN, true);
  $hash = hash_hmac('sha256', $data_check_string, $secret_key);
  if (strcmp($hash, $check_hash) !== 0) {
    throw new Exception('Data is NOT from Telegram');
  }
  if ((time() - $auth_data['auth_date']) > 86400) {
    throw new Exception('Data is outdated');
  }
  return $auth_data;
}
function saveTelegramUserData($auth_data) {
  $auth_data_json = json_encode($auth_data);
  setcookie('tg_user', $auth_data_json);
}
try {
  $auth_data = checkTelegramAuthorization($_GET);
  saveTelegramUserData($auth_data);
} catch (Exception $e) {
  die ($e->getMessage());
}
header('Location: login_example.php');
?>

I can send the hash (via querystring) and will compare it against check_hash if (strcmp($hash, $check_hash) !== 0) {. This is a real timing attack vulnerability!

Thanks for that answer, it was helpful to get my thoughts onto the right process.

You are welcome :)