Python 中的 Shopify HMAC 参数验证失败

Shopify HMAC parameter verification failing in Python

我在验证来自 Shopify 的 HMAC 参数时遇到了一些问题。我根据 Shopify documentation 使用的代码是 return 一个不正确的结果。

这是我的注释代码:

import urllib
import hmac
import hashlib

qs = "hmac=96d0a58213b6aa5ca5ef6295023a90694cf21655cf301975978a9aa30e2d3e48&locale=en&protocol=https%3A%2F%2F&shop=myshopname.myshopify.com&timestamp=1520883022"

解析查询字符串

params = urllib.parse.parse_qs(qs)

提取hmac值

value = params['hmac'][0]

根据文档从查询字符串中删除参数

del params['hmac']
del params['signature']

重新组合参数

new_qs = urllib.parse.urlencode(params)

计算摘要

h = hmac.new(SECRET.encode("utf8"), msg=new_qs.encode("utf8"), digestmod=hashlib.sha256)

Returns False!

hmac.compare_digest(h.hexdigest(), value)

从表面上看,最后一步应该 return 正确。此处遵循的每个步骤都在 Shopify 文档中进行了概述。

我要 post 回答我自己的问题,因为在梳理了 Shopify 的论坛和 SO 的其余部分之后,我找不到任何可以明确回答这个问题的东西。

最近,Shopify 开始在查询字符串负载中包含 protocol 参数。这本身不是问题, 除了 因为 Shopify 在他们的文档中忽略了提到 :/ 不是 [=33] =] 检查签名时。这是完全荒谬的,因为他们自己 do URL-encode 他们提供的查询字符串中的这些字符。

因此,要解决此问题,只需将 safe 参数提供给 urllib.parse.urlencode 且值为 :/(合适,对吗?)。完整的工作代码如下所示:

params = urllib.parse.parse_qsl(qs)
cleaned_params = []
hmac_value = dict(params)['hmac']

# Sort parameters
for (k, v) in sorted(params):
    if k in ['hmac', 'signature']:
        continue

    cleaned_params.append((k, v))

new_qs = urllib.parse.urlencode(cleaned_params, safe=":/")
secret = SECRET.encode("utf8")
h = hmac.new(secret, msg=new_qs.encode("utf8"), digestmod=hashlib.sha256)

# Compare digests
hmac.compare_digest(h.hexdigest(), hmac_value)

希望这对其他运行进入这个问题的人有所帮助!

请注意,此代码有些简化,因为查询参数未按字典顺序(字母顺序)排序。如果这可以更清楚,我也可以更新代码来做到这一点。

import hmac
import hashlib


...

# Inside your view in Django's views.py
params = request.GET.dict()
#

myhmac = params.pop('hmac')
params['state'] = int(params['state'])
line = '&'.join([
    '%s=%s' % (key, value)
    for key, value in sorted(params.items())
])
print(line)
h = hmac.new(
    key=SHARED_SECRET.encode('utf-8'),
    msg=line.encode('utf-8'),
    digestmod=hashlib.sha256
)

# Cinderella ?
print(hmac.compare_digest(h.hexdigest(), myhmac))