Вы почти правы, за исключением того, что вам не нужно json.dumps
данные запроса.Скорее всего, это приведет к изменениям в выводе, таким как изменения в форматировании, которые не будут соответствовать исходным данным, что означает отказ HMAC.
Например,
{"id":"fd87d909-fbfc-466c-964a-5478d5bc066a"}
отличается от:
{
"id":"fd87d909-fbfc-466c-964a-5478d5bc066a"
}
, что на самом деле:
{x0ax20x20"id":"fd87d909-fbfc-466c-964a-5478d5bc066a"x0a}
Хеш будет полностью отличаться для двух входов.
Посмотрите, как json.loads
и json.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....
Это не помогает, что пример 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
для получения необработанного байтового ввода и простое использование encode
на secret
str для получения закодированных байтов (вместо использования подробного экземпляра 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;
?>