Signature Validation

The Dolby.io Communications APIs provides the ability for your webhook listener service to validate the integrity of an incoming event. Webhook signature validation is optional but allows you to check the signature and expiration to prevent your service from malicious actions including replay attacks. Validation ensures that the webhook payloads were actually sent by the trusted source and that the contents have not been modified.

A webhook signature is included in the request's header and can be used in your code to verify the request. The Dolby.io Communications APIs use a private key to generate a signature; the private key changes according to a pre-defined rotation period. Your webhook listener service can use the public key to validate that the payload has been signed by the matching private key. The public key is available through public HTTP access.

Validating a webhook event

Webhook event validation is performed by verifying:

  • Timestamp - Verify that the timestamp is not expired.
  • Signature - Verifying that the signature indicates that the message has the correct origin, is authentic and that neither the timestamp nor the message body have been tampered with, as the signature is generated using the concatenation of the two values.

The https://github.com/dolbyio-samples/webhook-signature-validation repository contains code samples with step-by-step details on verifying the webhook signature and tips on handling various use cases.

The client should perform the following steps to validate a webhook event:

StepData SourceNotes
Retrieve the timestampHTTP HeaderIf the timestamp cannot be retrieved, discard the message as it may have been altered and the header elements are missing.
Check the timestampCheck if the timestamp is older than the configured expiration period.

If it is older, discard the message as this could indicate a replay attack or a message with an altered timestamp.

See Preventing replay attacks for more information on timestamp tolerance.
Retrieve the key IDHTTP HeaderIf the key ID cannot be retrieved, discard the message as it may have been altered and the header elements are missing.
Retrieve the signatureHTTP HeaderIf the signature cannot be retrieved, discard the message as it may have been altered and the header elements are missing.
Retrieve the entire JSON bodyHTTP bodyIf the message body is empty, discard the message, as there is no content to validate.
Read public keyLocal cache or public location at Dolby.io Communications API endpointAttempt to retrieve the public verification key from local cache with the key ID from the header.

If you are unable to retrieve the public verification key, invalidate the cache and try to retrieve the public verification key from the Dolby.io Communications API endpoint: https://api.dolby.io/v1/public/keys/webhooks. If you are unable to retrieve the public verification key from the Dolby.io Communications API endpoint, discard the message, as it may have been tampered with and the key ID in the header may have been altered.

Store the new public key in a local cache.
Validate signatureUse a library of your choice to validate that the signature in the header is matching the public key and payload data composed of the concatenation of the timestamp, a dot and the JSON message body without reformatting.

For example:
1621927459.{"webhook":"test"}
If you are unable to validate the signature, discard the message, as its authenticity cannot be assured.

If the signature is valid, the message is authentic and unaltered.

The following is an example of a message that shows the header information and the message body:

Message:
	{"webhook":"test"}
Header Key:
	Dolby-Signature
Header Value:
	t=1621927459,k=6422B054-BE27-4E1F-B829-9377B9A37E78,s=4HvlZWUzlGAv0UFj6E2jhB4Ndq/0YUgp/q6ppoodhbF+N6W721oA40VzG7G9PJMJIcI90mwtGZDQMAf0rHpIBQ==

Key rotation

The Dolby.io Communications API platform rotates keys on a regular basis to maintain security best practices. Key rotation occurs at Dolby's discretion and should happen at least every three months. The key rotation transition period can last more than one hour, during which the old and new keys may be used alternatively. At the beginning of the transition period, the new key is deployed and coexists with the old one; emitted webhooks can be signed by either key. At the end of the transition period, the old key is completely removed and only the new key is used for signatures.

How key rotation works

1. A new private and public key pair is generated.
2. The new public key is served by the API alongside the old public key.
3. Then the new private key is pushed to Dolby servers and messages are then signed using the new private key, and the corresponding new public key ID is indicated in the request header.
4. Your local public key cache will fail to return the key corresponding to the new public key ID provided in the request header.
5. Query the Dolby.io Communications APIs to retrieve the public key corresponding to the new public key ID provided in the request header.
If this step fails, the message may have been tampered with and the key ID present in the header has been altered. The message should be discarded.
6. Invalidate your local public key cache.
7. Save the new public key in your local cache.
8. Proceed with message validation.
9. After the transition period, the Dolby.io Communications APIs will stop serving the deprecated old public key.

Retrieving the corresponding public key

Public keys can be retrieved from:  https://api.dolby.io/v1/public/keys/webhooks

Querying the API will return a JSON object consisting of key-value pairs, where the key is the public key ID and the value is the public key, for example:

{
	"E8DD5357-59E3-499E-A48B-361984AC8B98": "nmKL25z2q1dTjKAZMKfk/klqbAIc1BuXF8GJf3CPBKk=",
	"6422B054-BE27-4E1F-B829-9377B9A37E78": "Py25BIWn6tiJuaFScMEJO9j4wNeW5c/Ip5MHZ1KClUQ=",
}

Preventing replay attacks

The timestamp in the request's header can be used to prevent a replay attack, where a valid payload and signature are intercepted. The timestamp is the epoch time in seconds at the time of the webhook signature. Clients can use this timestamp to verify if the message has expired based on the timestamp tolerance set in your security implementation. Also, since the timestamp is part of the signed payload, and verified by the signature, any change to the timestamp will invalidate the signature. If the signature is valid, the client can still discard the message if the the timestamp is too old and the message has expired. If the timestamp is not older than the expiration period, the message can be forwarded to the code that will check the signature to verify that the timestamp and message body were not altered by a malicious third-party. Dolby recommends using an expiration period of 10 minutes.

The client should check the timestamp before the signature for the following reasons:

  • Checking the signature is more performance intensive than checking the timestamp.
  • Messages that have been tampered with are less likely to occur than valid ones.
  • If a message has been tampered with to change the timestamp, the message will be discarded, as the signature checking will reveal the tampering.