forked from Gear4music/sendgrid-php
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: verify signature from event webhook
Brings in the changes raised in this PR - sendgrid#969
- Loading branch information
Showing
8 changed files
with
222 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,5 +14,4 @@ test/prism/* | |
.vscode | ||
prism* | ||
temp.php | ||
example*.php | ||
TODO.txt | ||
TODO.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php | ||
|
||
use SendGrid\EventWebhook\EventWebhook; | ||
use SendGrid\EventWebhook\EventWebhookHeader; | ||
|
||
|
||
function isValidSignature($request) | ||
{ | ||
$publicKey = 'base64-encoded public key'; | ||
|
||
$eventWebhook = new EventWebhook(); | ||
$ecPublicKey = $eventWebhook->convertPublicKeyToECDSA($publicKey); | ||
|
||
return $eventWebhook->verifySignature( | ||
$ecPublicKey, | ||
$request->getContent(), | ||
$request->header(EventWebhookHeader::SIGNATURE), | ||
$request->header(EventWebhookHeader::TIMESTAMP) | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<?php | ||
|
||
namespace SendGrid\EventWebhook; | ||
|
||
use EllipticCurve\Ecdsa; | ||
use EllipticCurve\PublicKey; | ||
use EllipticCurve\Signature; | ||
|
||
/** | ||
* This class allows you to use the Event Webhook feature. Read the docs for | ||
* more details: https://sendgrid.com/docs/for-developers/tracking-events/event | ||
* | ||
* @package SendGrid\EventWebhook | ||
*/ | ||
class EventWebhook | ||
{ | ||
/** | ||
* Convert the public key string to a ECPublicKey. | ||
* | ||
* @param string $publicKey verification key under Mail Settings | ||
* @return PublicKey public key using the ECDSA algorithm | ||
*/ | ||
public function convertPublicKeyToECDSA($publicKey) | ||
{ | ||
return PublicKey::fromString($publicKey); | ||
} | ||
|
||
/** | ||
* Verify signed event webhook requests. | ||
* | ||
* @param PublicKey $publicKey elliptic curve public key | ||
* @param string $payload event payload in the request body | ||
* @param string $signature value obtained from the | ||
* 'X-Twilio-Email-Event-Webhook-Signature' header | ||
* @param string $timestamp value obtained from the | ||
* 'X-Twilio-Email-Event-Webhook-Timestamp' header | ||
* @return bool true or false if signature is valid | ||
*/ | ||
public function verifySignature($publicKey, $payload, $signature, $timestamp) | ||
{ | ||
$timestampedPayload = $timestamp . $payload; | ||
$decodedSignature = Signature::fromBase64($signature); | ||
|
||
return Ecdsa::verify($timestampedPayload, $decodedSignature, $publicKey); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?php | ||
|
||
namespace SendGrid\EventWebhook; | ||
|
||
/** | ||
* This class lists headers that get posted to the webhook. Read the docs for | ||
* more details: https://sendgrid.com/docs/for-developers/tracking-events/event | ||
* | ||
* @package SendGrid\EventWebhook | ||
*/ | ||
abstract class EventWebhookHeader | ||
{ | ||
const SIGNATURE = "X-Twilio-Email-Event-Webhook-Signature"; | ||
const TIMESTAMP = "X-Twilio-Email-Event-Webhook-Timestamp"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,20 @@ | ||
<?php | ||
/** | ||
* Allows us to include one file instead of two when working without composer. | ||
* | ||
* | ||
* PHP Version - 5.6, 7.0, 7.1, 7.2 | ||
* | ||
* @package SendGrid\Tests | ||
* @author Elmer Thomas <[email protected]> | ||
* @copyright 2018 SendGrid | ||
* @license https://opensource.org/licenses/MIT The MIT License | ||
* @version GIT: <git_id> | ||
* @link http://packagist.org/packages/sendgrid/sendgrid | ||
* @link http://packagist.org/packages/sendgrid/sendgrid | ||
*/ | ||
require_once __DIR__ . '/SendGrid.php'; | ||
require_once __DIR__ . '/contacts/Recipient.php'; | ||
require_once __DIR__ . '/contacts/RecipientForm.php'; | ||
require_once __DIR__ . '/eventwebhook/EventWebhook.php'; | ||
require_once __DIR__ . '/mail/Asm.php'; | ||
require_once __DIR__ . '/mail/Attachment.php'; | ||
require_once __DIR__ . '/mail/BatchId'; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
<?php | ||
|
||
namespace SendGrid\Tests\Unit; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use SendGrid\EventWebhook\EventWebhook; | ||
|
||
/** | ||
* This class tests the EventWebhook functionality. | ||
* | ||
* @package SendGrid\Tests\Unit | ||
*/ | ||
class EventWebhookTest extends TestCase | ||
{ | ||
const PUBLIC_KEY = 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEDr2LjtURuePQzplybd | ||
C+u4CwrqDqBaWjcMMsTbhdbcwHBcepxo7yAQGhHPTnlvFYPAZFceEu/1FwCM/QmGUhA=='; | ||
const PAYLOAD = '{"category":"example_payload","event":"test_event","message_id":"message_id"}'; | ||
const SIGNATURE = 'MEUCIQCtIHJeH93Y+qpYeWrySphQgpNGNr/U+UyUlBkU6n7RAwIgJTz2 | ||
C+8a8xonZGi6BpSzoQsbVRamr2nlxFDWYNH2j/0='; | ||
const TIMESTAMP = '1588788367'; | ||
|
||
public function testVerifySignature() | ||
{ | ||
$isValidSignature = $this->verify( | ||
self::PUBLIC_KEY, | ||
self::PAYLOAD, | ||
self::SIGNATURE, | ||
self::TIMESTAMP | ||
); | ||
|
||
$this->assertTrue($isValidSignature); | ||
} | ||
|
||
public function testBadKey() | ||
{ | ||
$isValidSignature = $this->verify( | ||
'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqTxd43gyp8IOEto2LdIfjRQrIbsd4S | ||
XZkLW6jDutdhXSJCWHw8REntlo7aNDthvj+y7GjUuFDb/R1NGe1OPzpA==', | ||
self::PAYLOAD, | ||
self::SIGNATURE, | ||
self::TIMESTAMP | ||
); | ||
|
||
$this->assertFalse($isValidSignature); | ||
} | ||
|
||
public function testBadPayload() | ||
{ | ||
$isValidSignature = $this->verify( | ||
self::PUBLIC_KEY, | ||
'payload', | ||
self::SIGNATURE, | ||
self::TIMESTAMP | ||
); | ||
|
||
$this->assertFalse($isValidSignature); | ||
} | ||
|
||
public function testBadSignature() | ||
{ | ||
$isValidSignature = $this->verify( | ||
self::PUBLIC_KEY, | ||
self::PAYLOAD, | ||
'signature', | ||
self::TIMESTAMP | ||
); | ||
|
||
$this->assertFalse($isValidSignature); | ||
} | ||
|
||
public function testBadTimestamp() | ||
{ | ||
$isValidSignature = $this->verify( | ||
self::PUBLIC_KEY, | ||
self::PAYLOAD, | ||
self::SIGNATURE, | ||
'timestamp' | ||
); | ||
|
||
$this->assertFalse($isValidSignature); | ||
} | ||
|
||
private function verify($publicKey, $payload, $signature, $timestamp) | ||
{ | ||
$eventWebhook = new EventWebhook(); | ||
$ecPublicKey = $eventWebhook->convertPublicKeyToECDSA($publicKey); | ||
return $eventWebhook->verifySignature( | ||
$ecPublicKey, | ||
$payload, | ||
$signature, | ||
$timestamp | ||
); | ||
} | ||
} |