PHP HMAC - 这个hmac验证器的实现是否足够?

时间:2014-06-08 14:48:09

标签: php security hmac

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

该类包含除API_KEY之外的所有变量,validNonce()是为每个版本更改的已定义常量,以及在使用公钥加密进行初始三向交换后保存在DB中的共享密钥。设备注册期间共享密钥。 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; } 只需查找数据库中的nonce即可查看它是否有效。

我的问题归结为:我是否在正确的轨道上?我错过了明显出血的东西吗?

{{1}}

1 个答案:

答案 0 :(得分:0)

  

我走对了吗?我是否遗漏明显的东西?

Timing attacks好吧,所以确实并不明显,但这是这里缺少的难题。

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

<android.support.design.widget.AppBarLayout
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingTop="@dimen/appbar_padding_top"
    android:theme="@style/AppTheme.AppBarOverlay">
    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/toolbarheight"
        android:background="@drawable/navigation_bar_top"
        app:popupTheme="@style/AppTheme.PopupOverlay">
        <LinearLayout
            android:layout_width="260dp"
            android:layout_height="match_parent"
            android:orientation="horizontal">
            <android.support.design.widget.TabLayout
                android:id="@+id/tabs"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@drawable/background_toolbar_main" />
        </LinearLayout>
    </android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>

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

- return $this->hash === $hmac_hash;
+ return hash_equals($this->hash, $hmac_hash);

当我设计PASETO时,这是我的关注点,因此我定义了一种称为PAE(预身份验证编码)的方案,该方案使这些消息变得清晰而明确。您可以找到实现here(如下所示):

$data = API_KEY . $this->device_key . $this->user_key . 
        $this->cnonce . $this->nonce . $this->body;

以及兼容的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<~
}

这意味着您希望最终结果在创建和验证HMAC标签时使用 PAE 之类的东西将所有片段编码为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; } 函数。不要忘记使用$data来比较两个字符串。