如何在Python中验证HMAC摘要

问题描述 投票:0回答:1

我需要验证使用 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 对其进行了清理

https://emn178.github.io/online-tools/sha256.html?input_type=utf-8&input=%7B%0A%20%20%22applicantId%22%20%3A%20%2264899a9e52e16960a43a2d8c%22%2C %0A%20%20%22inspectionId%22%20%3A%20%2264899a9e52e16960a43a2d8d%22%2C%0A%20%20%22申请人类型%22%20%3A%20%22个人%22%2C%0A%20%20 %22correlationId%22%20%3A%20%229e732c5f814e27b766d20a85d23d169c%22%2C%0A%20%20%22levelName%22%20%3A%20%22basic-kyc-level%22%2C%0A%20%20%22sandbox模式%22%20%3A%20true%2C%0A%20%20%22externalUserId%22%20%3A%20%22uOev8DYqbH%22%2C%0A%20%20%22type%22%20%3A%20%22申请人已创建%22%2C%0A%20%20%22reviewStatus%22%20%3A%20%22init%22%2C%0A%20%20%22createdAt%22%20%3A%20%222024-04-17%2006 %3A57%3A33%2B0000%22%2C%0A%20%20%22创建AtMs%22%20%3A%20%222024-04-17%2006%3A57%3A33.165%22%2C%0A%20%20 %22clientId%22%20%3A%20%22uab_first_digital_trade_europe%22%0A%7D&hmac_enabled=1&hmac_input_type=utf-8&hmac_key=TZUQlLdW-E5VM7nbcByTbyQx9G_

任何人都可以帮助我了解如何让它发挥作用吗?

谢谢你

编辑

我尝试直接在 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}'
python python-3.x hmac
1个回答
0
投票

第二个片段中的代码看起来不错。只需确保您比较的是正确的有效负载和签名 - “编辑 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()
    更可取,因为它可以防止定时攻击。

© www.soinside.com 2019 - 2024. All rights reserved.