[英]PHP HMAC - Will this implementation of a hmac validator be sufficient?
以下函數是Request類的一部分。 該類基本上只保存從標頭和正文解析的信息,我想實現一個安全的HMAC驗證方案。 我以前沒有做過,但是我在這里和其他地方都讀過很多關於這個主題的文章。 我選擇sha256算法作為性能和安全性之間的中間方法。
該類包含除API_KEY
以外的所有變量, API_KEY
是定義的常量,對於每個版本都會更改,並且我在與設備進行注冊的過程中使用公共密鑰加密進行了最初的三向交換之后,將存儲在數據庫中的共享密鑰保密。 validNonce()
只是在數據庫中查找隨機數,以查看其是否有效。
我的問題可以歸結為:我走對了嗎? 我是否遺漏明顯的東西?
public function isValidRequest($secret)
{
if(!validNonce($this->nonce))
{
return false;
}
$data = API_KEY . $this->device_key . $this->user_key .
$this->cnonce . $this->nonce . $this->body;
$hmac_hash = hash_hmac("sha256",$data,$secret);
return $this->hash === $hmac_hash;
}
我在正確的軌道上嗎? 我是否遺漏明顯的東西?
定時攻擊 ! 好的,這並不是很明顯,但這是這里缺少的難題。
通用解決方案(以diff格式)是:
- return $this->hash === $hmac_hash;
+ return hash_equals($this->hash, $hmac_hash);
此外,在處理多部分消息時,應仔細考慮如何將數據提供給HMAC 。 否則,即使您安全地使用HMAC,也會引入從其組成部分的不同組合創建兩個相同字符串的風險,這可能會影響安全性。
$data = API_KEY . $this->device_key . $this->user_key .
$this->cnonce . $this->nonce . $this->body;
當我設計PASETO時,這是我的關注點,因此我定義了一種稱為PAE (預身份驗證編碼)的方案,該方案使這些消息變得清晰而明確。 您可以在這里找到一個實現(轉載如下):
<?php
class Util
{
// ~8<~8<~8<~8<~8<~ SNIP SNIP ~8<~8<~8<~8<~8<~
/**
* Format the Additional Associated Data.
*
* Prefix with the length (64-bit unsigned little-endian integer)
* followed by each message. This provides a more explicit domain
* separation between each piece of the message.
*
* Each length is masked with PHP_INT_MAX using bitwise AND (&) to
* clear out the MSB of the total string length.
*
* @param string ...$pieces
* @return string
*/
public static function preAuthEncode(string ...$pieces): string
{
$accumulator = \ParagonIE_Sodium_Core_Util::store64_le(\count($pieces) & PHP_INT_MAX);
foreach ($pieces as $piece) {
$len = Binary::safeStrlen($piece);
$accumulator .= \ParagonIE_Sodium_Core_Util::store64_le($len & PHP_INT_MAX);
$accumulator .= $piece;
}
return $accumulator;
}
// ~8<~8<~8<~8<~8<~ SNIP SNIP ~8<~8<~8<~8<~8<~
}
以及兼容的JavaScript實現:
function LE64(n) {
var str = '';
for (var i = 0; i < 8; ++i) {
if (i === 7) {
// Clear the MSB for interoperability
n &= 127;
}
str += String.fromCharCode(n & 255);
n = n >>> 8;
}
return str;
}
function PAE(pieces) {
if (!Array.isArray(pieces)) {
throw TypeError('Expected an array.');
}
var count = pieces.length;
var output = LE64(count);
for (var i = 0; i < count; i++) {
output += LE64(pieces[i].length);
output += pieces[i];
}
return output;
}
這意味着您希望最終結果在創建和驗證HMAC標簽時使用PAE之類的東西將所有片段編碼為$data
函數。 不要忘記使用hash_equals()
比較兩個字符串。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.