Twilio Notify deliveryCallbackUrl嵌套请求签名验证正确格式
问题描述
我在开发基于Twilio Notify的批量短信通知功能时,为追踪单条消息发送状态,给notification资源配置了deliveryCallbackUrl,创建通知的代码如下:
$notification = $service->notifications->create([ "toBinding" => $binding, "body" => $message_text, "deliveryCallbackUrl" => "https://username:password@mysite/notification_delivery_callback.php" ]);
我可以正常接收到回调payload,但做请求合法性校验时始终验证失败,验证逻辑代码如下:
use Twilio\Rest\Client; use Twilio\Security\RequestValidator; $configs = get_configs(); $signature = $_SERVER["HTTP_X_TWILIO_SIGNATURE"]; $request_url = $_SERVER['SCRIPT_URI']; $validator = new RequestValidator($configs['twilio_auth_token']); $request = $_REQUEST; // $is_valid 始终返回false $is_valid = $validator->validate($signature, $request_url, $request);
接收到的payload为嵌套结构,包含JSON格式内容,结构示例如下:
Array( [NotificationSid] => NTxxxxxxxxx [ServiceSid] => ISxxxxxxxxx [Count] => 4 [IsFinal] => true [DeliveryState] => Array( [3] => {"status":"SENT","type":"sms","identity":"xxxxxxxxx","sid":"SMxxxxxxxxx"} [2] => {"status":"SENT","type":"sms","identity":"xxxxxxxxx","sid":"SMxxxxxxxxx"} [1] => {"status":"SENT","type":"sms","identity":"xxxxxxxxx","sid":"SMxxxxxxxxx"} [0] => {"status":"SENT","type":"sms","identity":"xxxxxxxxx","sid":"SMxxxxxxxxx"} ) [AccountSid] => ACxxxxxxxxx [SequenceId] => 0 )
经排查DeliveryState嵌套数组是导致验证失败的原因:其他非嵌套结构的webhook请求都可以正常完成验证。查阅Twilio官方文档暂未找到嵌套请求的相关验证规则,先后尝试数组扁平化、JSON字符串转义、两种方案组合使用等处理方式,均无法通过验证,尝试过的参数格式如下:
Array( ... [DeliveryState] => Array( [0] => "{\"status\":\"SENT\",\"type\":\"sms\",\"identity\":\"xxxxxxxxx\",\"sid\":\"SMxxxxxxxxx\"}" [1] => "{\"status\":\"SENT\",\"type\":\"sms\",\"identity\":\"xxxxxxxxx\",\"sid\":\"SMxxxxxxxxx\"}" [2] => "{\"status\":\"SENT\",\"type\":\"sms\",\"identity\":\"xxxxxxxxx\",\"sid\":\"SMxxxxxxxxx\"}" [3] => "{\"status\":\"SENT\",\"type\":\"sms\",\"identity\":\"xxxxxxxxx\",\"sid\":\"SMxxxxxxxxx\"}" ) ... )
Array( ... [0] => "{\"status\":\"SENT\",\"type\":\"sms\",\"identity\":\"xxxxxxxxx\",\"sid\":\"SMxxxxxxxxx\"}" [1] => "{\"status\":\"SENT\",\"type\":\"sms\",\"identity\":\"xxxxxxxxx\",\"sid\":\"SMxxxxxxxxx\"}" [2] => "{\"status\":\"SENT\",\"type\":\"sms\",\"identity\":\"xxxxxxxxx\",\"sid\":\"SMxxxxxxxxx\"}" [3] => "{\"status\":\"SENT\",\"type\":\"sms\",\"identity\":\"xxxxxxxxx\",\"sid\":\"SMxxxxxxxxx\"}" ... )
需要确认该类嵌套结构请求完成签名验证所需的正确参数格式。
原因与解决方案
验证失败由两个共同问题导致:
- 基准URL不匹配:回调URL携带了基础认证凭据(
https://username:password@mysite/...),Twilio计算签名时会自动剔除URL中的userinfo段(即username:password@部分),直接取$_SERVER['SCRIPT_URI']会把认证信息带入签名计算,直接导致签名不匹配。 - 参数结构处理错误:Twilio的webhook签名逻辑只识别表单提交时的原始一维键值对,不会处理多维嵌套结构。PHP自动解析的
$_REQUEST会把DeliveryState[0]这类表单键转换成嵌套数组,和Twilio签名时使用的参数结构不一致;额外做的数组扁平化、JSON转义操作属于多余处理,反而破坏了原始参数的键值格式。
按以下步骤调整即可通过验证:
- 手动指定正确的基准URL,剔除认证信息,确保和Twilio侧配置的回调地址完全一致(协议、域名、路径、端口都要匹配,反向代理场景不要使用内网回源地址)。
- 直接使用PHP原生解析的
$_POST一维数组传入验证器,不要手动嵌套参数、不要转义JSON值、不要重新排序键。PHP原生$_POST默认保留了表单提交时的原始键名(即DeliveryState[0]这类格式)和原始值,不需要额外处理。
正确的验证代码如下:
use Twilio\Security\RequestValidator; $configs = get_configs(); $signature = $_SERVER["HTTP_X_TWILIO_SIGNATURE"]; // 手动指定剔除了认证信息的正确回调地址 $request_url = "https://mysite/notification_delivery_callback.php"; $validator = new RequestValidator($configs['twilio_auth_token']); // 直接传入原始$_POST一维数组,不要用嵌套的$_REQUEST,不要做参数转义/重构 $is_valid = $validator->validate($signature, $request_url, $_POST);
传入验证器的参数数组正确格式参考:
Array( [NotificationSid] => NTxxxxxxxxx, [ServiceSid] => ISxxxxxxxxx, [Count] => 4, [IsFinal] => true, [DeliveryState[3]] => {"status":"SENT","type":"sms","identity":"xxxxxxxxx","sid":"SMxxxxxxxxx"}, [DeliveryState[2]] => {"status":"SENT","type":"sms","identity":"xxxxxxxxx","sid":"SMxxxxxxxxx"}, [DeliveryState[1]] => {"status":"SENT","type":"sms","identity":"xxxxxxxxx","sid":"SMxxxxxxxxx"}, [DeliveryState[0]] => {"status":"SENT","type":"sms","identity":"xxxxxxxxx","sid":"SMxxxxxxxxx"}, [AccountSid] => ACxxxxxxxxx, [SequenceId] => 0 )
注意事项:
- 所有参数保持一维结构,不要做嵌套处理
DeliveryState对应的子项键名保留DeliveryState[索引]的原始表单格式,不要修改索引顺序DeliveryState各键对应的值直接使用收到的原始JSON字符串,不要加反斜杠转义双引号,不要提前解析成PHP数组
内容的提问来源于stack exchange,提问作者tracey armstrong
相关产品推荐
相关产品推荐

