如何在 PHP 中验证 Paypal webhook 签名?
Posted
技术标签:
【中文标题】如何在 PHP 中验证 Paypal webhook 签名?【英文标题】:How to verify Paypal webhook signature in PHP? 【发布时间】:2016-05-17 09:28:51 【问题描述】:我对 SSL 和证书不是很了解。我用了帖子 "How to use hash_hmac() with "SHA256withRSA" on php?" 看看我是否可以使用 PayPal 获取 webhook。
我遇到的问题是在调用openssl_verify()
并返回结果为 (0) 后出现以下错误:
OpenSSL 错误 openssl_verify 错误:04091068:rsa 例程:INT_RSA_VERIFY:bad signature
我已尝试解决此问题,但有关错误和网络功能的文档很少。
我当前的代码如下所示:
// get the header post to my php file by PayPal
$headers = apache_request_headers();
// get the body post to me php file by PayPal
$body = @file_get_contents('php://input');
$json = json_decode($body);
// TransmissionId|TransmissionTimeStamp|WebhookId|CRC32 as per PayPal documentation
$sigString = $headers['Paypal-Transmission-Id'].'|'.$headers['Paypal-Transmission-Time'].'|'.$json->id.'|'.crc32($body);
// $headers['Paypal-Cert-Url'] contains the "-----BEGIN CERTIFICATE---MIIHmjCCBoKgAwIBAgIQDB8 ... -----END CERTIFICATE-----"
$pubKey = openssl_pkey_get_public(file_get_contents($headers['Paypal-Cert-Url']));
// and this is the call to verify that returns result (0)
$verifyResult = openssl_verify($sigString, base64_decode($headers['Paypal-Transmission-Sig']), $pubKey, 'sha256WithRSAEncryption');
与我使用的参考代码唯一不同的是,我不使用openssl_pkey_get_details($pubKey)
,因为除了现有的签名错误之外,我还会得到以下错误:
OpenSSL 错误 openssl_verify 错误:0906D06C:PEM 例程:PEM_read_bio:no start line OpenSSL 错误 openssl_verify 错误:04091068:rsa 例程:INT_RSA_VERIFY:bad signature
我还尝试了一种变体,即不在标头上使用base64_decode()
,但会得到相同的返回结果 (0) 并显示错误:
OpenSSL 错误 openssl_verify 错误:04091077:rsa routines:INT_RSA_VERIFY:wrong signature length
签名有什么问题?
【问题讨论】:
我相信你不应该使用$json->id
,因为这是事件的ID,而不是网络钩子的ID。网络挂钩 ID 不会随事件一起发送 - 您必须在此处使用常量。
【参考方案1】:
公式是<transmissionId>|<timeStamp>|<webhookId>|<crc32>
而不是<transmissionId>|<timeStamp>|<eventId>|<crc32>
。另请注意,无法验证 Webhook 模拟器事件。
【讨论】:
【参考方案2】:这可能不是您正在寻找的,但是使用 Open SSL 手动验证签名的替代方法可能是使用 PayPal PHP Restful API。
PayPal Restful API 公开了一个端点,允许您验证 webhook:/v1/notifications/verify-webhook-signature
PayPal-PHP-SDK 提供了一个 VerifyWebhookSignature
类,可以轻松调用该端点。
他们还有一个Sample Script 说明如何使用VerifyWebhookSignature
类。
【讨论】:
【参考方案3】:你可能想要使用这段代码:
$pubKey = openssl_pkey_get_public(file_get_contents($headers['PAYPAL-CERT-URL']));
$details = openssl_pkey_get_details($pubKey);
$verifyResult = openssl_verify($sigString, base64_decode($headers['PAYPAL-TRANSMISSION-SIG']), $details['key'], 'sha256WithRSAEncryption');
if ($verifyResult === 0)
throw new Exception('signature incorrect');
elseif ($verifyResult === -1)
throw new Exception('error checking signature');
【讨论】:
我猜@Maxime Rainville 建议的带有官方 SDK 的解决方案是一个更好的主意。即使您需要为此执行另一个 HTTP 调用。以上是关于如何在 PHP 中验证 Paypal webhook 签名?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 PHP Omnipay 和 Paypal Express Checkout 验证付款?