Verifying webhook requests

How to securely receive calls from the Pismo platform.

The Pismo platform sends webhook messages to your services when an event requires a response. For example, Pismo must request approval from you before it can authorize a purchase. Pismo's data streaming platform handles other types of events.

Webhook requests use JSON Web Tokens (JWTs). Each token is signed with a public/private key pair. Pismo retains the private key and makes the public key available to clients. Each token also includes a body hash that can be used to verify the integrity of the payload.

📘

For information about handling other types of events, see the Data and reporting overview.

Overview of the verification process

The following diagram summarizes the process of verifying a webhook request from the Pismo platform.

  1. A request received from Pismo will contain a JWT. The JWT will contain a signature, along with the name of a public key that can be used to validate the signature.

  2. The client service should check the cache to see if it contains a public key with that name. If not, it should request a new list of public keys from the Pismo platform.

  3. Once the client finds the specified public key, it can use the key to verify the signature. If the signature is successfully verified, the client can be sure that the request really came from Pismo.

  4. After verifying the signature, the client should verify that the message payload has not been changed in route to the client server. To do this, the JWT includes a hash. The client should run the message payload through SHA256 encryption. The result should match the hash.

The following sections provide a more detailed explanation of these steps.

Parsing the JWT

When the Pismo platform sends an HTTP request to a client server, the request includes an Authorization header containing a JWT. You can use this token to authenticate and authorize the request, but you first need to parse it.

JWT is an open standard, and there are many great open-source libraries for parsing a JWT, verifying the cryptographic signature, and retrieving the data contained in the token. Pismo recommends that you use a well maintained and tested library. Here are some good choices:

For other languages, go to https://jwt.io/. If you need further assistance, you can contact your Technical Account Manager or open a ticket with the Service Desk.

A JWT token contains the claims listed in the table below. You can use them for the next steps in the verification process.

keyvaluedescription
algRS256The algorithm used to generate the JWT signature.
kid*The ID of the key used to generate the signature. This must be one of the public keys provided by the Pismo platform. See Retrieving a list of public keys. Example: 1f88b81429cc451a335c2f5cdb3dfb34eb3bbc7f
issapi.pismo.ioThe issuer of the JWT.
subThe subject claim is the Account ID of the account associated with the request. Example: 1000001
audShort for audience. The hostname of the intended recipient of the token. Example: https://www.example.com
iatThe time of the JWT generation in seconds since the UNIX epoch. Example: 1584990931
expThe time at which the token expires in seconds since the UNIX epoch. It can be a maximum of 3600 seconds later than the iat. Example: 1584990931
body_hashA SHA256 digest of the base64 encoded request message body. Example: dX5GKSLmA9o7lijdQrm7kR4om6rLTHssk1GJQiDGuUk=

*Not all JWTs contain a kid.

Retrieving a list of public keys

Pismo uses private and public keys to sign JWTs. You don't need to worry about the private keys – those are only used by Pismo – but you will need to get a list of the public keys. You do this by calling the public keys endpoint.

# These are endpoint where you can get Pismo's public keys:
Sandbox: curl -i https://sandbox.pismolabs.io/robot/v1/metadata/x509/[email protected]
Production: curl -i https://prod.pismo.io/robot/v1/metadata/x509/[email protected]

Once you get the list of public keys, you can cache it and use it to verify the signatures of subsequent requests.

🚧

"Should I call the public keys endpoint for every request?"

No. The public keys endpoint has a rate limit of 5 requests per second. To avoid getting rate limited, the public keys endpoint should only be used in the two scenarios listed below.

You should only call the public keys endpoint in the following scenarios:

  1. You don’t have the list of public keys yet. You should call the public keys endpoint when you first log in.

  2. To renew an expired list, according to the period specified in the Renewing your list of public keys section.

Renewing your list of public keys

The public keys in your list will eventually expire, so your service must include logic for refreshing the public keys. The Pismo platform relies on the HTTP cache-control mechanism to let you know when it's time to renew the public keys in your cache, so you can look at the Cache-Control header for instructions on when to call the public keys endpoint again.

The max-age component tells you the number of seconds that the requested content will remain valid.

Here's a sample of the contents of a cache-control header:

cache-control: public, max-age=22040, must-revalidate, no-transform

In this case, you can wait 22040 seconds (6 hours and 12 minutes) before renewing the cached public keys.

🚧

If you can't find the specified public key in your list of public keys, your list has expired and you need to renew it.

Verifying the token signature

After parsing the JWT, you need to verify that it was signed by Pismo. Start by finding out which public key the Pismo platform used to sign the request by looking for the kid (key id) claim in the token.

Next, check to see if you already have the specified key in your local cache. If so, you're ready to verify the signature.

If you don't already have the key, request a new list of public keys using the public keys endpoint. See Retrieving a list of public keys.

Once you have the specified public key, you can use it to verify the signature. Pismo recommends that you use one of the libraries listed in the Parsing the JWT section to do this. For an example of the signature verification process, go to JSON Web Signature (JWS) with RSA.

If the signature matches, this verifies that the request came from Pismo, and you can proceed to the next step.

️ Signature not matching

If the key names match, but the signature doesn't, it may be the result of some sort of attack. Deny the request immediately and notify the security team.

📘

A JWT might not contain a kid.

In this case, you must parse over all available public keys and try verifying the signature with each one until you find one that matches.

Checking message integrity

The next step is to make sure the request payload was not modified on the way to your server. The JWT includes a hash of its payload. This lets you check the integrity of the message.

You begin by getting the body_hash value from the JWT. This is an SHA256 hash of the HTTP message body (usually in JSON).

Next, you need to generate the hash for the message payload. You do this by encoding the message in the base64 format and then applying an SHA256 hash function to it. The result is the computed hash.

Lastly, you need to compare the computed hash with the body_hash value. If they are equal, the verification process is complete.


Did this page help you?