我需要验证使用 HMAC 发送的数据摘要。
我已经被困在这个问题上几天了,我无法匹配Python中的摘要,这是我的代码:
import hmac
import hashlib
import json
# i will receive, a webhook that looks like this
payload = {
"applicantId" : "64899a9e52e16960a43a2d8c",
"inspectionId" : "64899a9e52e16960a43a2d8d",
"applicantType" : "individual",
"correlationId" : "9e732c5f814e27b766d20a85d23d169c",
"levelName" : "basic-kyc-level",
"sandboxMode" : true,
"externalUserId" : "uOev8DYqbH",
"type" : "applicantCreated",
"reviewStatus" : "init",
"createdAt" : "2024-04-17 06:57:33+0000",
"createdAtMs" : "2024-04-17 06:57:33.165",
"clientId" : "uab_first_digital_trade_europe"
}
plain_text_payload = ','.join(f"{k}:{str(v).lower()}" if isinstance(v, bool) else f"{k}:{v}" for k, v in payload.items())
encoded_payload = plain_text_payload.encode('utf-8')
secret_key = 'TZUQlLdW-E5VM7nbcByTbyQx9G_'
encoded_secret = secret_key.encode('utf-8')
computed_signature = hmac.new(encoded_secret, encoded_payload, hashlib.sha256).hexdigest()
print("Computed HMAC signature:", computed_signature)
# This is the siguature Im expecting
signature = "84ea2fd80f8972554a2f7c1d7e97823abff09a32bd9af3865251f696fe0f31af"
if computed_signature == signature:
print("Signatures match.")
else:
print("Signatures do not match.")
现在,上面的代码不起作用,因为“true”是小写的。 我尝试将其设置为布尔值 True,然后将其作为文本转换回小写,但什么也没有,如果我在它周围加上引号,仍然不匹配。
但是,我使用以下工具成功获得了预期的签名:
https://www.devglan.com/online-tools/hmac-sha256-online
对于以下内容,更新秘密,在其末尾添加“_”,出于某种原因,Stack Overflow 对其进行了清理
任何人都可以帮助我了解如何让它发挥作用吗?
谢谢你
编辑
我尝试直接在 json 负载上执行此操作:
def validate_webhook():
# Map the header to the appropriate hash function name
algo_map = {
'HMAC_SHA1_HEX': hashlib.sha1,
'HMAC_SHA256_HEX': hashlib.sha256,
'HMAC_SHA512_HEX': hashlib.sha512,
}
# Get the algorithm from the request header
algo_name = request.headers.get('X-Payload-Digest-Alg')
if algo_name not in algo_map:
log_message(f"Unsupported algorithm: {algo_name}", "ALERT", run_id=g.run_id)
raise RuntimeError('Unsupported algorithm')
# Get the hash function based on the algorithm
hash_func = algo_map[algo_name]
secret_key = 'TZUQlLdW-E5VM7nbcByTbyQx9G_'
# Calculate the HMAC for the content
content = request.get_data() # Gets the raw data as bytes
computed_signature = hmac.new(secret_key.encode(), content, hash_func).hexdigest()
# Get the signature sent in the request headers
signature = request.headers.get('X-Signature')
# Check if the computed signature matches the one in the header
if computed_signature != signature:
log_message(f"Webhook sumsub sign {content}", "ALERT", run_id=g.run_id)
raise Exception('Webhook sumsub sign validation failed')
# Return some response or handle the valid request further
log_message(content.decode('utf-8'), "INFO", run_id=g.run_id, data=content.decode('utf-8') )
return True
我也尝试将有效负载转换为文本:
def validate_webhook():
# Map the header to the appropriate hash function name
algo_map = {
'HMAC_SHA1_HEX': hashlib.sha1,
'HMAC_SHA256_HEX': hashlib.sha256,
'HMAC_SHA512_HEX': hashlib.sha512,
}
# Get the algorithm from the request header
algo_name = request.headers.get('X-Payload-Digest-Alg')
if algo_name not in algo_map:
log_message(f"Unsupported algorithm: {algo_name}", "ALERT", run_id=g.run_id)
raise RuntimeError('Unsupported algorithm')
# Get the hash function based on the algorithm
hash_func = algo_map[algo_name]
secret_key = 'TZUQlLdW-E5VM7nbcByTbyQx9G_'
# Calculate the HMAC for the content
content = request.get_data(as_text=True) # Gets the data as a text string
# Explicitly encode the content to UTF-8 before computing HMAC
encoded_content = content.encode('utf-8')
computed_signature = hmac.new(secret_key.encode(), encoded_content, hash_func).hexdigest()
# Get the signature sent in the request headers
signature = request.headers.get('X-Signature')
# Check if the computed signature matches the one in the header
if computed_signature != signature:
log_message(f"Webhook sumsub sign {content}", "ALERT", run_id=g.run_id)
raise Exception('Webhook sumsub sign validation failed')
# exit if the signature is valid
log_message(content, "INFO", run_id=g.run_id, data=content )
return True
编辑2
这是我收到的有效负载:
b'{\n "applicantId" : "6616c4f80cc65e2a1fe1419d",\n "inspectionId" : "6616c4f80cc65e2a1fe1419e",\n "applicantType" : "individual",\n "correlationId" : "7a3450416467276914de43eb75b5916a",\n "sandboxMode" : true,\n "externalUserId" : "level-2ce713de-679a-453e-9719-2464082633c5",\n "type" : "applicantCreated",\n "reviewStatus" : "init",\n "createdAt" : "2024-04-17 08:28:07+0000",\n "createdAtMs" : "2024-04-17 08:28:07.745",\n "clientId" : "uab_first_digital_trade_europe"\n}'
第二个片段中的代码看起来不错。只需确保您比较的是正确的有效负载和签名 - “编辑 2”中的有效负载与在线工具链接中的有效负载不同,因此签名也会不同。当我在与在线工具相同的有效负载上运行第二个片段时,它会产生相同的签名。
如果您需要继续调试,我建议您(暂时)记录相关值:
print("content = {!r}".format(content))
print("expected signature = {!r}".format(signature))
print("computed_signature = {!r}".format(computed_signature))
这将为您提供一组一致的值,您可以稍后对其运行测试。
一些补充说明:
正如评论中所指出的,您需要非常小心编码。最好的方法是只读取原始字节(就像您在第二个片段中所做的那样)。解码为 UTF-8,然后像在第三个片段中那样重新编码是自找麻烦。并且像在第一个片段中那样手动编码为 JSON(或使用
json.dumps()
)是完全不可能的(除非您自己生成数据,这里似乎不是这种情况),因为即使是最小的数据差异(例如空格)将导致签名不同。
理想情况下,您应该使用
if not hmac.compare_digest(computed_signature, signature): raise ...
而不是 if computed_signature != signature: raise ...
来比较签名。它们返回相同的结果,但 compare_digest()
更可取,因为它可以防止定时攻击。