Shopify HMAC parameter verification failing in Python
--------------------------------------------------
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
and get $2,000 discount on your first invoice
--------------------------------------------------
Music by Eric Matyas
https://www.soundimage.org
Track title: Life in a Drop
--
Chapters
00:00 Shopify Hmac Parameter Verification Failing In Python
01:01 Accepted Answer Score 8
01:52 Answer 2 Score 1
02:04 Thank you
--
Full question
https://stackoverflow.com/questions/4924...
--
Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...
--
Tags
#python #shopify #hmac
#avk47
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
and get $2,000 discount on your first invoice
--------------------------------------------------
Music by Eric Matyas
https://www.soundimage.org
Track title: Life in a Drop
--
Chapters
00:00 Shopify Hmac Parameter Verification Failing In Python
01:01 Accepted Answer Score 8
01:52 Answer 2 Score 1
02:04 Thank you
--
Full question
https://stackoverflow.com/questions/4924...
--
Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...
--
Tags
#python #shopify #hmac
#avk47
ACCEPTED ANSWER
Score 8
At some point, recently, Shopify started including the protocol parameter in the querystring payload. This itself wouldn't be a problem, except for the fact that Shopify doesn't document that : and / are not to be URL-encoded when checking the signature. This is unexpected, given that they themselves do URL-encode these characters in the query string that is provided.
To fix the issue, provide the safe parameter to urllib.parse.urlencode with the value :/ (fitting, right?). The full working code looks like this:
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)
Hope this is helpful for others running into this issue!
ANSWER 2
Score 1
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))