PYTHON

Verifying Webhook Signatures in Python Flask

Secure your Flask application's webhook endpoints by verifying incoming signatures to ensure data integrity and authenticity from trusted sources.

import hmac
import hashlib
import json
from flask import Flask, request, abort

app = Flask(__name__)

# Your secret key for verifying webhooks.
# This should be a strong, randomly generated string,
# stored securely (e.g., in environment variables).
WEBHOOK_SECRET = 'your_super_secret_webhook_key'

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    # 1. Get the raw request body
    request_body = request.get_data()

    # 2. Get the signature from the header. The exact header name
    #    will depend on the service sending the webhook (e.g., 'X-Hub-Signature', 'X-Stripe-Signature').
    #    For demonstration, let's assume 'X-My-Signature'.
    signature_header = request.headers.get('X-My-Signature')
    if not signature_header:
        abort(400, description='Signature header missing')

    # 3. Calculate the expected signature
    #    The signature algorithm (e.g., 'sha256') and encoding (e.g., 'hex')
    #    also depend on the webhook provider.
    expected_signature = hmac.new(
        WEBHOOK_SECRET.encode('utf-8'),
        msg=request_body,
        digestmod=hashlib.sha256
    ).hexdigest()

    # 4. Compare the expected signature with the received one
    #    Use hmac.compare_digest for constant-time comparison to prevent timing attacks.
    if not hmac.compare_digest(expected_signature, signature_header):
        abort(403, description='Invalid webhook signature')

    # 5. If signature is valid, process the webhook payload
    try:
        payload = json.loads(request_body)
        print(f"Received valid webhook: {json.dumps(payload, indent=2)}")
        # Perform your webhook processing here (e.g., update database, trigger events)
        return {'status': 'success'}, 200
    except json.JSONDecodeError:
        abort(400, description='Invalid JSON payload')

if __name__ == '__main__':
    # To run:
    # 1. pip install Flask
    # 2. python your_script_name.py
    # 3. Send a POST request with the 'X-My-Signature' header
    #    You'll need to generate a signature for testing:
    #    Example:
    #    import hmac, hashlib, json
    #    secret = 'your_super_secret_webhook_key'.encode('utf-8')
    #    payload = {'event': 'test.event', 'data': {'id': 123}}
    #    body = json.dumps(payload, separators=(',', ':')).encode('utf-8') # Ensure no whitespace
    #    signature = hmac.new(secret, msg=body, digestmod=hashlib.sha256).hexdigest()
    #    print(f"Signature: {signature}")
    #    curl -X POST -H "Content-Type: application/json" -H "X-My-Signature: <paste_signature_here>" -d '{"event":"test.event","data":{"id":123}}' http://127.0.0.1:5000/webhook
    app.run(debug=True)
How it works: This Flask snippet demonstrates how to verify webhook signatures, a crucial security measure. When a third-party service sends a webhook, it often includes a signature in the request headers, generated using a shared secret and the request body. This code retrieves the raw request body and the signature, calculates its own expected signature using the same algorithm, and then securely compares it to the received signature using `hmac.compare_digest`. If the signatures don't match, the request is rejected, preventing processing of potentially fraudulent or tampered webhook events.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs