簡體   English   中英

在 Python 中使用 Flask 進行 Hmac 驗證(在 PHP 和 RUBY 中參考)

[英]Hmac verification with flask in Python (with reference in PHP and RUBY)

我一直在研究一種在 python 中使用燒瓶為 Selly.gg 商家網站實現 HMAC 驗證的方法。

所以 Selly 的開發文檔給出了以下示例來驗證 HMAC 簽名(在 PHP 和 ruby​​ 中): https : //developer.selli.gg/? php#signing-validating(下面的代碼:)

PHP:

<?php
        $signature = hash_hmac('sha512', json_encode($_POST), $secret);
        if hash_equals($signature, $signatureFromHeader) {
            // Webhook is valid 
        }
?>

紅寶石:

signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha512'), secret, payload.to_json)
is_valid_signature = ActiveSupport::SecurityUtils.secure_compare(request.headers['X-Selly-Signature'], signature)

所以,到目前為止我能弄清楚:他們不使用 base64 編碼(就像 shopify 和其他人一樣),它使用 SHA-512,它將密碼與 json 響應數據一起編碼,最后請求標頭是“X-Selly” -簽名'

到目前為止,我已經編寫了以下代碼(基於 shopify 的 HMAC 簽名代碼https://help.shopify.com/en/api/getting-started/webhooks ):

SECRET = "secretkeyhere"
def verify_webhook(data, hmac_header):
    digest = hmac.new(bytes(SECRET, 'ascii'), bytes(json.dumps(data), 'utf8'), hashlib.sha512).hexdigest()
    return hmac.compare_digest(digest, hmac_header)
try:
    responsebody = request.json #line:22
    status = responsebody['status']#line:25
except Exception as e:
    print(e)
    return not_found()
print("X Selly sign: " + request.headers.get('X-Selly-Signature'))
verified = verify_webhook(responsebody, request.headers.get('X-Selly-Signature'))
print(verified)

然而,sally 有一個 webhook 模擬器,即使有正確的密鑰和有效的請求,verify_webhook 也將始終返回 False。 我嘗試聯系 Selly 支持,但他們幫不了我更多

您可以在以下地址測試 webhook 模擬器: https ://selly.io/dashboard/{your account}/developer/webhook/simulate

你幾乎是對的,只是你不需要json.dumps請求數據。 這可能會在輸出中引入更改,例如格式更改,這些更改與原始數據不匹配,這意味着 HMAC 將失敗。

例如

{"id":"fd87d909-fbfc-466c-964a-5478d5bc066a"}

不同於:

{
  "id":"fd87d909-fbfc-466c-964a-5478d5bc066a"
}

這實際上是:

{x0ax20x20"id":"fd87d909-fbfc-466c-964a-5478d5bc066a"x0a}

兩個輸入的散列將完全不同。

看看json.loadsjson.dumps將如何修改格式和哈希值:

http_data = b'''{
    "id":"fd87d909-fbfc-466c-964a-5478d5bc066a"
}
'''
print(http_data)
h = hashlib.sha512(http_data).hexdigest()
print(h)
py_dict = json.loads(http_data) # deserialise to Python dict
py_str = json.dumps(py_dict) # serialise to a Python str
py_bytes = json.dumps(py_dict).encode('utf-8') # encode to UTF-8 bytes
print(py_str)
h2 = hashlib.sha512(py_bytes).hexdigest()
print(h2)

輸出:

b'{\n    "id":"fd87d909-fbfc-466c-964a-5478d5bc066a"\n}\n'
364325098....
{"id": "fd87d909-fbfc-466c-964a-5478d5bc066a"}
9664f687a....

Selly 的 PHP 示例顯示了類似的內容,這無濟於事。 事實上,Selly PHP 示例是無用的,因為無論如何都不會對數據進行表單編碼,因此數據不會在$_POST

這是我的 Flask 小例子:

import hmac
import hashlib
from flask import Flask, request, Response

app = Flask(__name__)

php_hash = "01e5335ed340ef3f211903f6c8b0e4ae34c585664da51066137a2a8aa02c2b90ca13da28622aa3948b9734eff65b13a099dd69f49203bc2d7ae60ebee9f5d858"
secret = "1234ABC".encode("ascii") # returns a byte object

@app.route("/", methods=['POST', 'GET'])
def selly():
    request_data = request.data # returns a byte object
    hm = hmac.new(secret, request_data, hashlib.sha512)
    sig = hm.hexdigest()

    resp = f"""req: {request_data}
    sig: {sig}
    match: {sig==php_hash}"""

    return Response(resp, mimetype='text/plain')

app.run(debug=True)

請注意使用request.data來獲取原始字節輸入,並在secret str 上簡單使用encode來獲取編碼字節(而不是使用詳細的bytes()實例化)。

這可以通過以下方式進行測試:

curl -X "POST" "http://localhost:5000/" \
 -H 'Content-Type: text/plain; charset=utf-8' \
 -d "{\"id\":\"fd87d909-fbfc-466c-964a-5478d5bc066a\"}"

我還創建了一些 PHP 來驗證兩種語言創建相同的結果:

<?php
    header('Content-Type: text/plain');
    $post = file_get_contents('php://input');
    print $post;
    $signature = hash_hmac('sha512', $post, "1234ABC");
    print $signature;
?>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM