首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PHP HMAC -这个hmac验证器的实现足够了吗?

PHP HMAC -这个hmac验证器的实现足够了吗?
EN

Stack Overflow用户
提问于 2014-06-08 22:48:09
回答 1查看 305关注 0票数 1

以下函数是请求类的一部分。这个类基本上只保存从头部和主体解析的信息,我想实现一个安全的HMAC验证方案。我以前没有这样做过,但我已经阅读了很多关于这个主题的文章,无论是在这里还是在其他地方。我选择了sha256算法作为性能和安全性之间的折中方案。

该类保存所有变量,除了API_KEY和共享密钥之外的所有变量,前者是一个定义的常量,随每个版本而变化,后者是在与公钥加密进行初始三次交换后存储在DB中的,在设备注册期间保护共享密钥。validNonce()只是在数据库中查找现时值,看看它是否有效。

我的问题可以归结为:我在正确的轨道上吗?我是不是明显漏掉了什么?

代码语言:javascript
复制
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;
}
EN

回答 1

Stack Overflow用户

发布于 2018-09-13 03:49:24

我在正确的轨道上吗?我是不是明显漏掉了什么?

Timing attacks!好的,这不是很明显,但这是拼图中缺失的一部分。

一般的解决方案( diff格式)是:

代码语言:javascript
复制
- return $this->hash === $hmac_hash;
+ return hash_equals($this->hash, $hmac_hash);

此外,在处理多部分消息时,需要仔细考虑如何将数据馈送到HMAC中。否则,即使您正在安全地使用HMAC,也会带来从其组成部分的不同组合创建两个相同字符串的风险,这可能会影响安全性。

代码语言:javascript
复制
$data = API_KEY . $this->device_key . $this->user_key . 
        $this->cnonce . $this->nonce . $this->body;

这是我在设计PASETO时关心的问题,所以我定义了一种称为PAE (预认证编码)的方案,它使这些消息清晰明确。您可以找到一个实现here (如下所示):

代码语言:javascript
复制
<?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实现:

代码语言: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()来比较这两个字符串。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24107293

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档