Skip to main content

Validating Received Events

In order to be sure that Toggl is the one sending events to your URL endpoint we include a special HTTP header X-Webhook-Signature-256 that you can use to validate that no one else is sending those requests.

In order to perform this validation you should:

  • Set the field secret while creating a new subscription. If omitted, the system will assign one automatically.
  • When delivering events to your subscription's URL endpoint the system will add the X-Webhook-Signature-256 header.
  • Signature has the form of sha256={value} where value is a HMAC hash based on SHA256 algorithm + secret + full body.
  • Example: sha256=6d011bcd0b5bfb7e45372af01bc18f30cc04599df72eca189cdac1094008b095.

Code Examples

In the following examples we will assume that:

  1. We have a subscription where its secret field has the value PGuRrhCFajIyEvFlreKL.
  2. That we received this PING event:
"event_id": 0,
"created_at": "2022-06-25T03:58:10.207820267Z",
"creator_id": 6,
"metadata": {
"request_type": "POST",
"event_user_id": 6
"payload": "ping",
"subscription_id": 6,
"url_callback": "",
"timestamp": "2022-06-25T03:58:10.207820267Z"

Which in its raw form is really sent unformatted like this:

  1. That we also received the HTTP header x-webhook-signature-256 with the following signature value: sha256=bf829606cda0ca6923defb5ca70a43135adc7e8887486a201a19cb50ca6006b1.

We will then show in different languages how an end user can compute on their side the signature value using the received raw JSON value and the subscription's secret.

message='{"event_id":0,"created_at":"2022-06-25T03:58:10.207820267Z","creator_id":6,"metadata":{"request_type":"POST","event_user_id":6},"payload":"ping","subscription_id":6,"timestamp":"2022-06-25T03:58:10.207820267Z","url_callback":""}'; \
signature=$(echo 'sha256=bf829606cda0ca6923defb5ca70a43135adc7e8887486a201a19cb50ca6006b1' | sed 's/^.*=//'); \
secret=PGuRrhCFajIyEvFlreKL; \
if [[ $signature == $(echo -n $message | openssl dgst -sha256 -hmac $secret | sed 's/^.*= //') ]];
then echo "Valid HMAC";
else echo "Invalid HMAC";

Advanced Validation Techniques

The timestamp field

This field indicates the time when the server sent the event to your subscription URL endpoint. If your server does not answer with a 2xx HTTP status code, then our server will retry the event some time later, and the timestamp value will reflect on each case the current time at which our server is delivering it to the subscription endpoint. You can use this field to prevent replay attacks by disregarding messages with old timestamps (perhaps allowing some margin of a minute to account for network message propagation and network clock skewing).

The url_callback field

You can use this field to validate that the received message was intended for your subscription URL endpoint by comparing both values. If you have different webhooks subscriptions that share the same secret (not recommended) and some malicious actor has access to the events sent to one of your subscription URL endpoint, they could forward the same events to another one of your subscriptions endpoint and given that the secret is the same the HMAC validation would be valid. By comparing the value of url_callback in the received event with the expected one for that subscription URL you can prevent this kind of attacks. You may as well always use different secrets so that the HMAC validation fails if someone forwards a message from another subscription.

Other considerations

The Content-Type header sent by our server to your subscriptions URL endpoint will be application/json and the HTTP method will be POST. This is yet another check you can enforce to disregard unexpected messages arriving at your registered endpoints.

© 2024 Toggl. All rights reserved.