Orchestrate
Signature Verification

Webhook Signature Verification

Secure webhook endpoints with HMAC-SHA256 signature validation


Webhook signature verification ensures that incoming webhook requests are genuinely from Payload and haven't been tampered with. By validating cryptographic signatures, you protect your webhook endpoints from unauthorized access, replay attacks, and malicious requests that could trigger unintended actions in your system.

Prerequisites

Before implementing signature verification, it's helpful to learn about the following topics.


Why Verify Webhook Signatures


Webhook signature verification protects your application from several security threats:

Security Threats Without Verification

  • Unauthorized Requests: Attackers can send fake webhook requests to trigger actions
  • Replay Attacks: Old webhook payloads can be resent to cause duplicate processing
  • Data Tampering: Webhook payloads can be modified in transit
  • Phishing: Malicious actors can impersonate Payload to extract information
  • Resource Abuse: Fake webhooks can overload your system or trigger expensive operations

How Signature Verification Works

Webhook signature verification uses HMAC-SHA256 cryptographic hashing to create and validate signatures.

Signature Generation Process

Secret Key

You set a sender_secret when creating the webhook

Request Signing

Payload computes an HMAC-SHA256 hash of the webhook payload using your secret

Header Inclusion

The signature is included in the X-Payload-Signature HTTP header

Request Delivery

The webhook request is sent to your endpoint with the signature header

Verification Process

Extract Signature

Read the X-Payload-Signature header from the incoming request

Compute Hash

Calculate HMAC-SHA256 of the request body using your stored secret

Compare Signatures

Use constant-time comparison to match the signatures

Accept or Reject

Process the webhook if signatures match, reject if they don't

The signature algorithm is:

HMAC-SHA256(webhook_payload_json, sender_secret)

Setting Up Signature Verification

Configure webhook signature verification by setting a sender secret when creating webhooks.

import os
 
import payload
 
pl = payload.Session("secret_key_3bW9...", api_version="v2")
 
 
webhook = pl.Webhook.create(
    trigger="processed",
    url="https://yourdomain.com/webhooks/handler",
    sender_secret=os.getenv("WEBHOOK_SECRET"),
)
 
print(f"Webhook created: {webhook.id}")
print(f"Trigger: {webhook.trigger}")
print(f"URL: {webhook.url}")
print("Signature verification enabled")

This example creates a webhook with signature verification enabled:

  1. Set the sender_secret parameter to your chosen secret key
  2. Store this secret securely in your environment variables
  3. Payload will include the signature in the X-Payload-Signature header
  4. Your webhook handler must verify the signature before processing

Choosing a Sender Secret

Your sender secret should be:

  • Long: At least 32 characters for strong security
  • Random: Generated using a cryptographically secure random generator
  • Unique: Different for each webhook or environment
  • Secure: Stored in environment variables, never in source code
  • Rotatable: Easy to change if compromised

Example secret generation:

# Generate a secure random secret
openssl rand -hex 32

Implementing Signature Verification

Implement signature verification in your webhook handler to validate incoming requests.

import hmac
import hashlib
import json
import os
from flask import Flask, request, jsonify
 
app = Flask(__name__)
 
def verify_signature(payload, signature, secret):
    computed_signature = hmac.new(
        secret.encode('utf-8'),
        json.dumps(payload).encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
 
    return hmac.compare_digest(signature, computed_signature)
 
@app.route('/webhooks/handler', methods=['POST'])
def webhook_handler():
    signature = request.headers.get('X-Payload-Signature')
    webhook_data = request.json
 
    if not verify_signature(webhook_data, signature, os.environ.get('WEBHOOK_SECRET', 'your_secret_key')):
        return jsonify({'error': 'Invalid signature'}), 401
 
    # Signature valid - process webhook
    trigger = webhook_data['trigger']
    print(f'Valid webhook received: {trigger}')
 
    return jsonify({'status': 'received'}), 200
 
if __name__ == '__main__':
    app.run(port=3000)

Schema Reference


The signature verification field in webhook configuration:

Webhook Signature Configuration

sender_secret
string
Secret key for HMAC-SHA256 signature verification. When set, webhook requests will include an X-Payload-Signature header. This field is write-only and will not be returned in responses.
Max length: 64

Next Steps

Enhance webhook security and reliability after implementing signature verification


Enhance Webhook Security

Add OAuth Authentication for webhook endpoints requiring OAuth 2.0 tokens, implement API Security best practices to protect webhook endpoints, and use Secret Management systems to securely store sender secrets and rotate them periodically.

Monitor and Debug Webhooks

Troubleshoot webhook delivery with Debugging Webhooks to identify and resolve webhook issues, review webhook logs to monitor signature verification failures and delivery history, and implement error handling to gracefully manage webhook failures and retries.

Implement Webhooks

Monitor payment events with Transaction Webhooks to track payment and payout processing in real-time, review the Webhooks Overview for complete webhook setup and configuration, and consult the Webhook API Reference for detailed API documentation.


Related articles