【问题标题】:PHP JWT validating a secret key from a given token - why don't my 2 signatures match?PHP JWT 验证来自给定令牌的密钥 - 为什么我的 2 个签名不匹配?
【发布时间】:2021-04-17 14:43:37
【问题描述】:

我正在使用 PHP 解析从外部 API 返回的 JWT 令牌。 解析有效负载很容易,但我想首先检查令牌中使用的密钥是否与我服务器上的密钥匹配。这将作为我的握手。为此,我将使用我认为已使用的密钥重新创建令牌的签名部分(第三部分)。如果结果值匹配,那么我知道密钥匹配。

例如,我的密钥是:testsecretkey

返回给我的Token是:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IjIzMTc2MiIsIklkIjoiMTM0IiwiSWRQZXJzb24iOiIxMTkiLCJQZXJzb25lblJvbGxlbiI6Ilt7XCJSb2xsZVwiOjEsXCJJZFwiOjE0MTg5fSx7XCJSb2xsZVwiOjIsXCJJZFwiOjExOX0se1wiUm9sbGVcIjozLFwiSWRcIjoxMDY5MH1dIiwibmJmIjoxNjEwNDYxNjA2LCJleHAiOjE2MTA0NjUyMDYsImlhdCI6MTYxMDQ2MTYwNn0.Cqz2mtZ9g_L4BSYTQztzQ4W0VldAf-Ihok24jVVi8iE P>

如果我通过https://jwt.io/ 运行它并在“验证签名”框中输入“testscretkey”并单击“秘密 base64 编码”复选框,一切都会检查出来,并且令牌看起来是正确的。

回到我在 PHP 中的服务器上,我使用 "." 将令牌分成 3 部分 ($tokenParts)作为分隔符。编码签名(第 3 部分,上面令牌的最后一部分)显示...:

Cqz2mtZ9g_L4BSYTQztzQ4W0VldAf-Ihok24jVVi8iE

我将我的密钥加密应用于令牌部分 1 和 2:

$base64UrlSignature = base64_encode(
            hash_hmac(
                'sha256',                               //The correct enc type
                $tokenParts[0] . "." . $tokenParts[1],  //The first 2 parts of the token
                'testsecretkey',                        // The secret key that should have been used
                true
            )
);

...然后我将我的新签名 ($base64UrlSignature) 与原始编码签名 ($tokenParts[2]) 进行比较。如果两者都匹配,则确认握手。

但是,即使我使用了相同的键,值也不匹配。相反,我的新签名是:

K4Mx71y1yRdQ7xotyR78FzQU8J2xTc65wZbhGFE-s4s

如果我取消选中“秘密 base64 编码”复选框,则返回 jwt.io(原始令牌信息仍输入框中)我可以看到 JWT 令牌签名(蓝色)更改为与我相同的值新签名($base64UrlSignature)。但这对我来说毫无意义,因为我已经对我的新签名进行了 base64_encoded。

谁能帮我找出我哪里出错了?

谢谢

【问题讨论】:

    标签: php jwt token


    【解决方案1】:

    “秘密 base64 编码”表示您输入的秘密是 base64 编码的。当然,您输入的密钥(“testsecretkey”)不是真正的 base64,但它仍然可以有效解码。

    在 PHP 中模拟:

    function base64_encode_url($string) {
        return str_replace(['+','/','='], ['-','_',''], base64_encode($string));
    }
    
    function base64_decode_url($string) {
        return base64_decode(str_replace(['-','_'], ['+','/'], $string));
    }
    
    $signature = base64_encode_url(
        hash_hmac(
                "sha256",
                $tokenParts[0] . "." . $tokenParts[1],
                base64_decode_url("testsecretkey"),
                true
            )
    );
    

    此代码生成Cqz2mtZ...,与您在响应中获得的签名相匹配。请注意,您必须使用特殊的modified version of base64,它保证只会生成 URL 安全字符。

    您从中获取此响应的 API 可能希望您为密钥输入 base64 值,而不是纯文本字符串。

    【讨论】:

    • 对于 HMAC 的编码,必须使用 Base64url 而不是(默认)Base64,参见例如base64url_encode()。否则,Base64 编码结果在字符 +-/_ 以及填充 (=) 方面有所不同。如果密钥包含字符_-(但testsecretkey 并非如此),则类似的情况也适用于密钥的解码。
    • @Topaco 你是对的,我已经编辑了我的答案以包含这个功能。
    • 感谢您的回复。当我回到办公室时,我会试试这个。我实际上是在使用带有类似函数引用的 base64url_encode。我删除了那个位只是为了简化示例。但我没有考虑将 base64_decodeurl 应用于密钥。
    猜你喜欢
    • 2017-05-04
    • 2016-11-17
    • 2019-11-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-12
    • 2018-05-29
    • 2017-10-12
    • 2014-10-14
    相关资源
    最近更新 更多