Python实现的HMAC-SHA256签名逻辑转PHP后结果不一致如何解决
问题根因与修复方案
1 核心差异:JSON序列化结果不一致
哈希值不匹配最主要原因是Python和PHP的JSON序列化默认规则不同:
- Python
json.dumps()默认使用的分隔符为(', ', ': '),也就是逗号、冒号后都会加空格,序列化结果为{"user_id": "Frank", "query": "Dog"} - PHP
json_encode()默认使用的分隔符为(',', ':'),逗号、冒号后无空格,序列化结果为{"user_id":"Frank","query":"Dog"}
输入HMAC算法的原始字符串字节流完全不同,最终得到的签名自然不一致。
跨语言做签名校验时,永远不要依赖各自语言的默认序列化配置,必须显式统一序列化规则。
2 次要问题:不必要的UTF-8编码转换
PHP的utf8_encode()函数作用是将ISO-8859-1编码的字符串转为UTF-8,如果你当前PHP环境的默认编码已经是UTF-8(绝大多数现代环境默认配置),重复调用该函数会改变原始字符串的字节序列,进一步导致签名计算错误。
修复方案
方案1:对齐Python到PHP的序列化规则(更推荐,无空格JSON传输效率更高)
修改Python的序列化代码,显式指定分隔符和PHP对齐:
message_encoded = json.dumps(message, separators=(',', ':')).encode('utf-8')
PHP端无需调整json_encode,只需去掉多余的utf8_encode调用即可:
<?php $secret = "cd01985/813hip914Vnn04&1#11ai761"; $message = array( 'user_id' => "Frank", 'query' => "Dog" ); $message_encoded = json_encode($message); $hmac_signature = hash_hmac('sha256', $message_encoded, $secret, false); // 输出结果和原Python代码一致:ab742454c0158c3ec8d8c9c8965381567d45bee77e6f8d9aacb2239587285f87 ?>
方案2:对齐PHP到Python的默认序列化规则
如果不能修改Python代码,PHP端可以手动处理JSON序列化结果,匹配Python的默认分隔符规则:
$message_encoded = json_encode($message); // 替换冒号和逗号,添加空格匹配Python默认格式 $message_encoded = str_replace([':', ','], [': ', ', '], $message_encoded);
注意该方案只适用于JSON值不含冒号、逗号的简单场景,复杂场景建议统一显式指定序列化参数。
补充问题解答
Python json.dumps() 没有直接对应的PHP原生默认实现,因为两种语言的默认序列化配置存在多处差异,除了分隔符之外,还包括中文转义、斜杠转义、空数组/对象处理等规则。如果要完全对齐,需要两边同时显式指定所有序列化参数:
- Python侧显式配置
ensure_ascii、separators、sort_keys等参数 - PHP侧显式配置
JSON_UNESCAPED_UNICODE、JSON_UNESCAPED_SLASHES、JSON_FORCE_OBJECT等参数
额外建议:对JSON的键按固定顺序排序后再序列化,可以避免因为键顺序不同导致的序列化结果差异。
内容的提问来源于stack exchange,提问作者Fredrik Carenborn
相关产品推荐
相关产品推荐

