sodium_crypto_auth_verify vs hash_equals 用于令牌身份验证

sodium_crypto_auth_verify vs hash_equals for Token Authentication

我正在为大学最后一年的项目构建自己的小框架,我对这里的最佳实践感到非常困惑。我已经安静地阅读了很多文章并且有一个大概的想法但是一些澄清会更好。

我真的很喜欢PHP中的新钠扩展,但我有点困惑。

我正在为长期持久性 cookie 和密码重置创建拆分令牌身份验证。

我尽可能多地使用 Libsodium,因为它看起来非常安全,尽管它是新的而且没有很好的记录。

我正在创建一个像 selector:validator 这样的拆分令牌。 我想做的基本上是使用选择器来查询数据库,

然后我想比较两个散列 VS 散列来自 cookie 的纯文本版本然后比较(但后者意味着在某处保留密钥会产生问题)

我听说过很多文章,尤其是关于 Paragone 的文章,建议最好将令牌的散列版本存储在数据库中,将普通版本存储在 cookie 或令牌中。 这有什么真正的好处吗?

我创建了一个简单的 Token class:

class SplitToken extends Token
{

protected $selector;

protected $validator;

function __construct($selector=14, $validator=18)
{
    $this->selector = bin2hex(random_bytes($selector));
    $this->validator = bin2hex(random_bytes($validator));
    $this->key = random_bytes(SODIUM_CRYPTO_AUTH_KEYBYTES);
    $this->tokenHash = sodium_crypto_auth($this->validator, $this->key);
}

public function Set()
{
    $this->token = $this->selector.':'.$this->validator;
    return $this;
}

public function Get()
{
    return $this->token;
}

$sptoken = new SplitToken();
$token = $sptoken->set()->Get();
$dt = new DateTime('+ 2 months');
$expiry = $dt->getTimestamp();
//Gets bin2hex version of validator side of token for DB
$validatorHash = $token->GetValidatorHashHex();
$key = $token->GetKey();

//Store token In DB:
$query = "UPDATE Users SET Selector, Validator, Expiry 
WHERE Selector = :Selector and    Validator = :Validator and Expiry = :Expiry;
$stmt = $pdo->prepare($query);
$stmt->execute(
['Selector' => $token->GetSelector(), 
'Validator' => $validatorHash, 
'Expiry' => $expiry]);

现在临时令牌在数据库中。现在是设置 cookie 的时候了(当然,这同样适用于具有不同到期时间的 PW 重置。 在这里我感到困惑,我有两个选择:

选项 1:

//Store the hexed version of selector:validator in the cookie (but not hashed)
setcookie('auth_token', $token, $expiry, '/', 'CONST_DOMAIN', true, true);

//Where do I store the key?
//So far I am using JSON fuNCTION which gets key from the folder where it is stored:
$storedKey = Key::GetFromVault('auth_token');

if(isset($_COOKIE['auth_token')){
 $cookie = explode(':', $_COOKIE['auth_token'];

//Gets the User from the DB Where Selector = Selector
 $user = DB::SelectUser($user);
//If User exists
 if($user){
 //*** Checks the Hash separate from the query to avoid timing attack ***
 var_dump(sodium_crypto_auth_verify($user->Validator, $cookie[1], $storedKey);
 }

}

选项 2: 看起来更简单和更清晰一些,因为我只是比较两个散列,以后不需要担心密钥,但这意味着我必须将验证器的散列版本存储在 cookie 中:

$hashedToken = $token->GetSelector.':'.$token->GetValidatorHashHex();
setcookie('auth_token', $hashedToken, $expiry, '/', 'CONST_DOMAIN', true, true);

//Now On Request:
if(isset($_COOKIE['auth_token')){
 $cookie = explode(':', $_COOKIE['auth_token'];
 $user = DB::SelectUser($user);
 if($user){
 //Checks the Hash separate from the query to avoid timing attack
 var_dump(hash_equals($user->Validator, $cookie[1]);
 }

}

我知道这听起来可能很傻,但没什么区别, 但在选项 1 中,我将纯文本标记与散列版本进行比较,并将其与钠函数进行比较 对于选项 2,我正在比较两个哈希

如果我将相同的两个散列与钠函数进行比较,它 returns 为假,如果我将纯文本与散列与 hash_equals 进行比较,即使散列之前的标记是相同的returns 错误。

所以基本上:

1) 这有什么不同吗? 2) 我想为密钥存储找到一个简洁的解决方案,然后将 plain 存储在 cookie 中,但不确定如何

如有任何建议,我们将不胜感激。 很高兴澄清我的问题 谢谢

没有任何区别。

sodium_crypto_auth_verify() 所做的是计算哈希值,并将其与提供的哈希值进行比较。

只要是常数时间比较,这个和自己做没有区别