diff --git a/include/functions.inc b/include/functions.inc
index 71e0d8dcd5db6c204e66fb678e36fb35fa74137d..5aa092f2f9d122840367b49a3b06cfbbcbc0abf2 100644
--- a/include/functions.inc
+++ b/include/functions.inc
@@ -2674,6 +2674,47 @@ if (!function_exists('random_int')) {
   }
 }
 
+if (!function_exists('hash_equals')) {
+  /**
+   * Timing attack safe string comparison
+   *
+   * Fallback for PHP<5.6
+   *
+   * Compares two strings using the same time whether they're equal or not.
+   * This function should be used to mitigate timing attacks; for instance, when testing crypt() password hashes.
+   *
+   * @param string $known_string The string of known length to compare against
+   * @param string $user_string The user-supplied string
+   * @return boolean Returns TRUE when the two strings are equal, FALSE otherwise.
+   */
+  function hash_equals($known_string, $user_string)
+  {
+    if (func_num_args() !== 2) {
+      trigger_error('hash_equals() expects exactly 2 parameters, '.func_num_args().' given', E_USER_WARNING);
+      return NULL;
+    }
+    if (!is_string($known_string) || !is_string($user_string)) {
+      trigger_error('hash_equals(): Expected both parameters to be a string, '.gettype($known_string).' and '.gettype($user_string).' given', E_USER_WARNING);
+      return FALSE;
+    }
+    $known_string_len = strlen($known_string);
+    $user_string_len  = strlen($user_string);
+    if ($known_string_len !== $user_string_len) {
+      // use $known_string instead of $user_string to handle strings of different length.
+      $res = $known_string ^ $known_string;
+      // set $ret to 1 to make sure false is returned
+      $ret = 1;
+    } else {
+      $res = $known_string ^ $user_string;
+      $ret = 0;
+    }
+    for ($i = strlen($res) - 1; $i >= 0; $i--) {
+      $ret |= ord($res[$i]);
+    }
+    return ($ret === 0);
+  }
+}
+
 function ldap_escape_f($str, $ignore = '')
 {
   return ldap_escape($str, $ignore, LDAP_ESCAPE_FILTER);