PHP可防止时序攻击的字符串比较方法实现
什么是防时序攻击?比较两个字符串,从第一个字符比较,当遇到不相等的字符串时返回比较结果,整个比较过程结束。由此产生对于长度一样的字符串当第不一致的字符串所在位置不同时,比较耗时的时长和字符串所在位置相关。由此利用时长差别,可以猜测出不一致的字符是什么,毕竟字符数量有限。最终可以猜测出整个完整一致的字符串。
为了避免引发上面的漏洞产生,PHP语言采用hash_equals函数,比较两个字符串,无论字符串是否相等,函数的时间消耗是恒定的,这样可以有效的防止时序攻击。它是php内置函数。
这里是php实现这种毕竟方法的代码:
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Util;
/**
* String utility functions.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class StringUtils
{
/**
* This class should not be instantiated.
*/
private function __construct()
{
}
/**
* Compares two strings.
*
* This method implements a constant-time algorithm to compare strings.
* Regardless of the used implementation, it will leak length information.
*
* @param string $knownString The string of known length to compare against
* @param string $userInput The string that the user can control
*
* @return bool true if the two strings are the same, false otherwise
*/
public static function equals($knownString, $userInput)
{
// Avoid making unnecessary duplications of secret data
if (!is_string($knownString)) {
$knownString = (string) $knownString;
}
if (!is_string($userInput)) {
$userInput = (string) $userInput;
}
if (function_exists('hash_equals')) {
return hash_equals($knownString, $userInput);
}
$knownLen = self::safeStrlen($knownString);
$userLen = self::safeStrlen($userInput);
if ($userLen !== $knownLen) {
return false;
}
$result = 0;
for ($i = 0; $i < $knownLen; ++$i) {
$result |= (ord($knownString[$i]) ^ ord($userInput[$i]));
}
// They are only identical strings if $result is exactly 0...
return 0 === $result;
}
/**
* Returns the number of bytes in a string.
*
* @param string $string The string whose length we wish to obtain
*
* @return int
*/
public static function safeStrlen($string)
{
// Premature optimization
// Since this cannot be changed at runtime, we can cache it
static $funcExists = null;
if (null === $funcExists) {
$funcExists = function_exists('mb_strlen');
}
if ($funcExists) {
return mb_strlen($string, '8bit');
}
return strlen($string);
}
}