Secure Webhooks

Uploadcare provides an option to automatically send notifications when a file is uploaded to a public URL of your choosing (webhooks). Each webhook payload can be signed with a secret to ensure that the request comes from the expected sender. The resulting signature is included in the request header, so you can use it to validate that the request comes from Uploadcare.

Setting your signing secret

Set up your secret token in two places: Uploadcare and your server.

In Uploadcare, a signing secret can be added with a POST request when creating or updating a webhook:

curl -v -X POST -H "Content-Type: application/json" 
-H 'Authorization: Uploadcare.Simple pub_key:secret_key' 'https://api.uploadcare.com/webhooks/WEBHOOK_ID/' 
-d '{"signing_secret": "some-secret"}'

Note, here we use a simple authorization scheme for clarity. Stick to the Uploadcare auth scheme in production code.

The signing secret can be added in Dashboard Settings on the Webhooks page.

To get a list of webhooks and their IDs in a project, you can make a GET request:

curl -v -H 'Authorization: Uploadcare.Simple pub_key:secret_key' 'https://api.uploadcare.com/webhooks/'

Set up an environment variable on your server that stores the secret:

export UC_SIGNING_SECRET=your_signing_secret

Validate payload

Uploadcare uses the set secret token to create a hash signature that will be added to each request to your webhook endpoint with the X-Uc-Signature header.

The signature is generated with HMAC/SHA-256 algorithm, and the header has the following format: X-Uc-Signature: v1=SIGNATURE.

An example of a signature verification in Python:

import hashlib
import hmac
import os

UC_SIGNING_SECRET = os.environ['UC_SIGNING_SECRET']

# see payload example in callbacks section of
# https://uploadcare.com/api-refs/rest-api/v0.5.0/#operation/webhookCreate
webhook_body = '''{...}'''

# example of a X-Uc-Signature HTTP header value
x_uc_signature_header = (
    "v1=f4d859ed2fe47b9a4fcc81693d34e58ad12366a841e58a7072c1530483689cc0"
)
calculated_signature = (
    "v1="
    + hmac.new(
        UC_SIGNING_SECRET.encode("utf-8"), 
        webhook_body.encode("utf-8"), 
        hashlib.sha256
    ).hexdigest()
)
print("x_uc_signature_header", x_uc_signature_header)
print("calculated_signature", calculated_signature)

if calculated_signature in x_uc_signature_header:
    print("WebHook signature matches!")
else:
    print("WebHook signature mismatch!")