我在验证来自Shopify的HMAC参数时遇到了一些麻烦。我根据Shopify documentation使用的代码返回的结果不正确。
这是我带注释的代码:
import urllib
import hmac
import hashlib
qs = "hmac=96d0a58213b6aa5ca5ef6295023a90694cf21655cf301975978a9aa30e2d3e48&locale=en&protocol=https%3A%2F%2F&shop=myshopname.myshopify.com×tamp=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)
返回False
!
hmac.compare_digest(h.hexdigest(), value)
表面上,最后一步应该是真的。此处所遵循的每个步骤都在Shopify文档中进行了评论。
答案 0 :(得分:2)
我将回答我自己的问题,因为在通过Shopify的论坛和SO的其余部分进行梳理之后,我找不到任何能够明确回答这个问题的答案。
最近,Shopify开始在查询字符串有效负载中包含protocol
参数。这本身不会有问题,除之外,因为Shopify在文档中忽略了:
和/
在检查时不进行URL编码的事实签名。这是完全没有意义的,因为他们自己做在他们提供的查询字符串中对这些字符进行URL编码。
因此,要解决此问题,只需将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(), value)
希望这对遇到此问题的其他人有帮助!
请注意,此代码有些简化,因为查询参数不按字典顺序(按字母顺序)排序。如果这可以更清楚,我也可以更新代码来执行此操作。
答案 1 :(得分:0)
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))