
本文详解 Viber Bot 回调签名验证中常见的 HMAC-SHA256 签名失败问题,指出关键在于原始请求体必须以原始字节流(raw body)参与签名计算,而非经 PHP 自动解析/重编码后的 JSON 字符串,并提供可直接运行的 PHP 示例与注意事项。
本文详解 viber bot 回调签名验证中常见的 hmac-sha256 签名失败问题,指出关键在于原始请求体必须以**原始字节流(raw body)**参与签名计算,而非经 php 自动解析/重编码后的 json 字符串,并提供可直接运行的 php 示例与注意事项。
在 Viber Bot 的回调(Callback)机制中,为确保请求来源可信,Viber 会在 HTTP 请求头中携带 X-Viber-Content-Signature 字段,其值是使用 Bot 的 authentication token 作为密钥、对原始请求体(raw request body)进行 HMAC-SHA256 计算后得到的十六进制字符串(小写,无前缀)。许多开发者(如提问者)误将 $request->getContent() 返回的内容直接传入 hash_hmac(),却未意识到 Laravel 或其他框架可能已对内容做了隐式处理(如自动解码、空格规范化、UTF-8 BOM 清理等),导致签名不匹配。
核心要点是:签名必须基于未经任何修改的原始字节流(raw payload) —— 即服务端接收到的、一字不差的 HTTP 请求体原始二进制数据。
✅ 正确做法(Laravel 示例):
public function index(Request $request)
{
// 关键:获取原始原始请求体(raw body),避免框架自动解析干扰
$rawBody = file_get_contents('php://input');
// Viber 提供的 Bot Token(即 HMAC 密钥)
$token = '4453b6ac12345678-e02c5f12174805f9-daec9cbb5448c51f';
// 使用 hash_hmac 计算 HMAC-SHA256,并转为小写十六进制字符串
$signature = hash_hmac('sha256', $rawBody, $token);
// 返回签名用于比对(实际应用中应与 X-Viber-Content-Signature 头对比)
return response($signature, 200)->header('Content-Type', 'text/plain');
}
⚠️ 注意事项:
-
切勿使用
json_encode()或$request->json()->all()重构 JSON:Viber 发送的是紧凑格式(no whitespace, no line breaks),任何格式化(beautify)、键顺序调整或类型转换(如整数转字符串)都会导致哈希值完全不同; -
不要依赖
$_POST或$request->all():这些会触发 PHP 表单解析逻辑,完全丢失原始结构; - 确认请求体编码为 UTF-8 且无 BOM:若原始 payload 含 BOM,需提前 strip(但 Viber 官方发送内容默认无 BOM);
-
严格区分大小写与字符:例如
user_id中的=是 Base64 编码的一部分,不可省略或替换; -
调试建议:先用
file_get_contents('php://input')输出原始内容到日志,与 Viber 文档示例逐字节比对(推荐用bin2hex()或在线 hex 工具校验)。
总结:Viber 的 HMAC 签名验证本质是「字节级一致性校验」。只要确保参与 hash_hmac() 的 $data 参数与 Viber 实际发送的请求体字节完全一致,签名即可准确复现。牢记 —— 不是“JSON 字符串”,而是“原始请求体字节流”。










