sendgrid / sendgrid-php

The Official Twilio SendGrid PHP API Library
https://sendgrid.com
MIT License
1.49k stars 623 forks source link

Issue in Signed Event Webhook - PHP #1046

Open iprashant opened 3 years ago

iprashant commented 3 years ago

Issue Summary

I have followed the steps given in https://sendgrid.com/docs/for-developers/tracking-events/getting-started-event-webhook-security-features/ for PHP and used library mentioned in https://github.com/sendgrid/sendgrid-php/tree/main/lib/eventwebhook similarly checked unittest https://github.com/sendgrid/sendgrid-php/blob/main/test/unit/EventWebhookTest.php for event signature verification

Steps to Reproduce

  1. Went to https://app.sendgrid.com/settings/mail_settings and did test your integration with the ngrok url.
  2. Got request with header on ngrok url
  3. Output from verifySignature is false.

Code Snippet

$timestamp = $req->header("X-Twilio-Email-Event-Webhook-Timestamp");
$signature = $req->header("X-Twilio-Email-Event-Webhook-Signature");
$verifycationKey = "<Verification Key>";
$body = \json_encode($req->all())."\r\n";
\Log::info("+++++++++++++++++++");
\Log::info($body);
\Log::info($timestamp);
\Log::info($signature);

$event = new EventWebhook;

$publicKey = $event->convertPublicKeyToECDSA($verifycationKey);
\Log::info(serialize($publicKey));
$flag =$event->verifySignature($publicKey, $body, $signature, $timestamp);
\Log::info("flag====> ".$flag);
\Log::info("+++++++++++++++++++");

Technical details:

eshanholtz commented 3 years ago

We've seen a similar issue in our nodejs library recently: https://github.com/sendgrid/sendgrid-nodejs/issues/1238

Is this a multi-event webhook? If so, a carriage return and newline (\r\n) needs to be included between every event in the body. Example: https://github.com/sendgrid/sendgrid-nodejs/pull/1271/files#diff-570eee91600a9234a175a0acce452638ee2c7cc583be1a4aa75eb35ca8b9500cR48

iprashant commented 3 years ago

Yes we are having multi-event webhook. As per your comment we have modified the code as follows still not luck.

` $timestamp = $req->header("X-Twilio-Email-Event-Webhook-Timestamp"); $signature = $req->header("X-Twilio-Email-Event-Webhook-Signature"); $verifycationKey = ""; $body = \json_encode($req->all()); $body = str_replace("},{", "},\r\n{",$body)."\r\n"; \Log::info("+++++++++++++++++++"); \Log::info($body); \Log::info($timestamp); \Log::info($signature);

$event = new EventWebhook;

$publicKey = $event->convertPublicKeyToECDSA($verifycationKey); \Log::info(serialize($publicKey)); $flag =$event->verifySignature($publicKey, $body, $signature, $timestamp); \Log::info("flag====> ".$flag); \Log::info("+++++++++++++++++++"); `

shwetha-manvinkurke commented 3 years ago

@iprashant have you tried with the raw body?

$body = $request->getContent()
$body = str_replace("},{", "},\r\n{",$body)."\r\n";
iprashant commented 3 years ago

Hi Shwetha,

I have used PHP file_get_contents('php://input'); still no luck.

Thanks,

shwetha-manvinkurke commented 3 years ago

OK, we have an internal ticket to investigate the multi-event webhook scenario. Marking this as a bug in the meantime.

This issue has been added to our internal backlog to be prioritized. Pull requests and +1s on the issue summary will help it move up the backlog.

peter-at-bpt commented 3 years ago

This replacement worked for me on multi-event payloads instead of the one above:

$bodyContent = str_replace(
    "},\n{", // The raw string comes in with newlines in this spot
    "},\r\n{",
    $body // $request->getContent() can be used here for Laravel apps
)."\r\n";
thinkingserious commented 3 years ago

Thank you for taking the time to share your solution @peter-at-bpt!

jobveldhuis commented 3 years ago

Hey there, we at fonQ are running into the same issue with the signature verification and the solution of @peter-at-bpt unfortunately does not seem to work. What is the status of your internal investigation?

shyammsuvarna commented 3 years ago

webhook signed webhook getting false for function = isValidSignature

$publicKey = 'my key';

$input = '[{"DRUPAL_ROOT":"/home/gems2/git/harmony","OURHTTP_HOST":"gemslocal2.artofliving.org","OURREQUEST_URI":"/ca/civicrm/event/register","contact_id":"24495","countryCode":"ca","email":"paul@yopmail.com","event":"processed","newHash":"87858ab1e574e0be","newJobId":"39003277","newQueueId":11312,"newemail_id":"13842","send_at":0,"sg_event_id":"cHJvY2Vzc2VkLTIyOTY4OTQyLWlhcXlDLWhlUW5TRnc4LWtsX0dTOWctMA","sg_message_id":"iaqyC-heQnSFw8-kl_GS9g.filterdrecv-7489454b79-khx4b-1-611E028F-2B.0","smtp-id":"iaqyC-heQnSFw8-kl_GS9g@geopod-ismtpd-6-0","timestamp":1629356687,"uniqueParam":"b.39003277.11312.87858ab1e574e0be@newsletter.india.artofliving.org"},{"DRUPAL_ROOT":"/home/gems2/git/harmony","OURHTTP_HOST":"gemslocal2.artofliving.org","OURREQUEST_URI":"/ca/civicrm/event/register","contact_id":"24495","countryCode":"ca","email":"paul@yopmail.com","event":"delivered","ip":"50.31.63.175","newHash":"87858ab1e574e0be","newJobId":"39003277","newQueueId":11312,"newemail_id":"13842","response":"250 mail saved","sg_event_id":"ZGVsaXZlcmVkLTAtMjI5Njg5NDItaWFxeUMtaGVRblNGdzgta2xfR1M5Zy0w","sg_message_id":"iaqyC-heQnSFw8-kl_GS9g.filterdrecv-7489454b79-khx4b-1-611E028F-2B.0","smtp-id":"iaqyC-heQnSFw8-kl_GS9g@geopod-ismtpd-6-0","timestamp":1629356688,"tls":0,"uniqueParam":"b.39003277.11312.87858ab1e574e0be@newsletter.india.artofliving.org"}]';

$headerStringValue = $_SERVER['HTTP_X_TWILIO_EMAIL_EVENT_WEBHOOK_SIGNATURE'];

$timestamp = $_SERVER['HTTP_X_TWILIO_EMAIL_EVENT_WEBHOOK_TIMESTAMP'];

and i am calling function like below

$result = isValidSignature($publicKey,$input,$headerStringValue,$timestamp);

i am getting result as false- but all values i am passing fine and also php library also fine.

help me to proceed , whats wrong in above code or any other setting i need to enable?

i have tested in postman by passing these details.

when i debugged - i am getting false from this function :

$success = openssl_verify($message, $signature->toDer(), $publicKey->openSslPublicKey, OPENSSL_ALGO_SHA256);

here its false , please help