集成 Paypal 的智能按钮后,我无法验证 Paypal 发送的 webhook 通知。我发现的示例要么已过时,要么不起作用。
有没有办法验证 webhook 通知,最好是 DIY 方式(即无需使用庞大而复杂的 Paypal API)?
为nodejs回答这个问题,因为有微妙的安全问题,有些在原始答案(但非常有用)中缺少逻辑。该答案解决了以下问题:
1.有人放置自己的URL,从而获得自己的请求的身份验证
nodejs <17.0缺少X509功能内置的一些。 4.理想情况下,应该用内置的证书链验证签名证书,但nodejs <17.0不能轻松地做到这一点。信托模型依赖于TLS和nodejs信托链的cert fetch url,而不是CERT URL返回的证书,这可能足够好。
const forge = require('node-forge'); const crypto = require('crypto') const CRC32 = require('crc-32'); const axios = require('axios'); const transmissionId = paypalSubsEvent.headers['PAYPAL-TRANSMISSION-ID']; const transmissionTime = paypalSubsEvent.headers['PAYPAL-TRANSMISSION-TIME']; const signature = paypalSubsEvent.headers['PAYPAL-TRANSMISSION-SIG']; const webhookId = '<your webhook ID from your paypal dev. account>'; const url = paypalSubsEvent.headers['PAYPAL-CERT-URL']; const bodyCrc32 = CRC32.str(paypalSubsEvent.body); const unsigned_crc = bodyCrc32 >>> 0; // found by trial and error // verify domain is actually paypal.com, or else someone // could spoof in their own cert const urlObj = new URL(url); if (!urlObj.hostname.endsWith('.paypal.com')) { throw new Error( `URL ${certUrl} is not in the domain paypal.com, refusing to fetch cert for security reasons`); } const validationString = transmissionId + '|' + transmissionTime + '|' + webhookId + '|' + unsigned_crc; const certResult = await axios.get(url); // Trust TLS to check the URL is really from *.paypal.com const cert = forge.pki.certificateFromPem(certResult.data); const publicKey = forge.pki.publicKeyToPem(cert.publicKey) const verifier = crypto.createVerify('RSA-SHA256'); verifier.update(validationString); verifier.end(); const result = verifier.verify(publicKey, signature, 'base64'); console.log(result);
为nodejs回答这个问题,因为有微妙的安全问题,有些在原始答案(但非常有用)中缺少逻辑。该答案解决了以下问题:
1.有人放置自己的URL,从而获得自己的请求的身份验证
nodejs <17.0缺少X509功能内置的一些。
4.理想情况下,应该用内置的证书链验证签名证书,但nodejs <17.0不能轻松地做到这一点。信托模型依赖于TLS和nodejs信托链的cert fetch url,而不是CERT URL返回的证书,这可能足够好。