26.5.4 Webhook and callback handlers

2025.10.06.
AI Security Blog

While much of AI system security focuses on the model and its primary APIs, asynchronous integrations like webhooks and callbacks often represent a softer, less-guarded flank. An external service—perhaps a data labeling platform or a transaction processor—completing a task and notifying your system via an HTTP POST request is a common pattern. This entry point, however, can become a direct conduit for attacks if not properly secured.

Threat Model: The Untrusted Messenger

A webhook handler is fundamentally an API endpoint that trusts an external entity to send well-formed, legitimate data. This trust is a vulnerability. From a red teaming perspective, your objective is to violate that trust to compromise the handler, the data it processes, or the downstream AI systems it feeds.

Kapcsolati űrlap - EN

Do you have a question about AI Security? Reach out to us here:

Core Assumption to Challenge: The system assumes that any request reaching the webhook URL is from the legitimate, expected service. Your goal is to prove this assumption false by forging, replaying, or manipulating these requests.

Key attack vectors include:

  • Payload Forgery: An attacker discovers the webhook endpoint and crafts a malicious payload. This could be designed to inject poisoned data into a training set, trigger a specific, harmful action in a production system (e.g., deleting a user account), or exploit a parsing vulnerability in the handler itself.
  • Server-Side Request Forgery (SSRF): If the handler processes URLs or makes requests based on payload data, an attacker can supply internal IP addresses or localhost URLs, forcing your server to scan its own network or interact with internal services.
  • Replay Attacks: A legitimate payload is captured and resent one or more times. This could cause duplicate processing, corrupt state, or trigger resource-intensive operations, leading to a denial-of-service condition.
  • Authentication Bypass: Many handlers rely on “security by obscurity,” where the endpoint URL contains a long, random string. Once discovered, this provides no real protection.

A Tale of Two Handlers: From Vulnerable to Hardened

Let’s examine how a simple webhook handler can be exploited and how to build a resilient one. We’ll use Python with the Flask framework for these examples.

The Vulnerable Handler

This is the kind of code you might find in a system built with a focus on functionality over security. It’s a wide-open door.

# WARNING: Insecure example for demonstration only.
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhook/process-data', methods=['POST'])
def process_data_webhook():
    # Assumes any JSON payload is valid and from a trusted source.
    data = request.get_json()
    
    # Directly processes the data, which could be malicious.
    result = process_with_ai_model(data['content']) 
    
    print(f"Processed data for user: {data.get('user_id')}")
    return jsonify({"status": "received"}), 200

# Fictional processing function
def process_with_ai_model(content):
    # Imagine this interacts with a database or model
    return {"result": "ok"}

This handler has no authentication, no payload verification, and no protection against replay attacks. An attacker who finds the /webhook/process-data URL can send any JSON they want.

The Hardened Handler

A secure handler never trusts the incoming request. It verifies the sender’s identity and the integrity of the payload before doing anything else.

import hmac
import hashlib
import time
from flask import Flask, request, jsonify, abort

app = Flask(__name__)
# This secret must be shared securely with the calling service.
WEBHOOK_SECRET = b'your-super-secret-key-here' 

@app.route('/webhook/secure-process-data', methods=['POST'])
def secure_process_data_webhook():
    # 1. Verify the signature
    signature = request.headers.get('X-Webhook-Signature')
    if not is_valid_signature(request.data, signature):
        abort(401, 'Invalid signature')

    # 2. Prevent replay attacks
    timestamp = request.headers.get('X-Webhook-Timestamp')
    if not is_recent(timestamp):
        abort(408, 'Request timestamp is too old')

    # 3. Now that we trust the source, parse the JSON
    data = request.get_json()
    
    # 4. (Optional but recommended) Queue for async processing
    # queue.add_task(process_with_ai_model, data['content'])

    return jsonify({"status": "accepted"}), 202

def is_valid_signature(payload, received_signature):
    if not received_signature:
        return False
    # Create a signature using the shared secret
    expected_signature = hmac.new(WEBHOOK_SECRET, payload, hashlib.sha256).hexdigest()
    # Securely compare the two signatures
    return hmac.compare_digest(expected_signature, received_signature)

def is_recent(timestamp_str):
    try:
        timestamp = float(timestamp_str)
        # Reject requests older than 5 minutes (300 seconds)
        return (time.time() - timestamp) < 300
    except (ValueError, TypeError):
        return False

This hardened version introduces three critical security controls: signature verification using a shared secret, timestamp validation to thwart basic replay attacks, and a clear separation between accepting the request and processing it.

Visualizing the Secure Flow

The interaction between the external service and the hardened handler follows a precise, verifiable sequence. This flow is designed to reject invalid requests at the earliest possible stage.

Secure Webhook Processing Flow Diagram External Service Webhook Handler 1. Verify Signature 2. Verify Timestamp 3. Validate Payload Schema 4. On Success: ACK (202) 5. On Failure: Reject (40x) 6. Enqueue Payload Secure Queue Worker Process HTTP POST Task Pull

Summary of Defenses

When assessing or building a webhook handler, prioritize defenses against the most common and impactful attack vectors. The following table provides a quick reference.

Vulnerability Attack Vector Example Primary Mitigation
Unauthenticated Access Attacker forges a request and sends it to the discovered webhook URL. HMAC Signature Verification: Use a shared secret to sign the request body, proving the sender’s identity and message integrity.
Replay Attack A valid request is intercepted and re-sent, causing duplicate processing. Timestamp/Nonce Validation: Include a timestamp or a unique, single-use nonce in the request and reject any that are too old or have been seen before.
Payload Injection Payload contains malicious data (e.g., SQL injection strings, poisoned training data). Strict Schema Validation: Define an explicit schema for the expected JSON payload and reject any request that does not conform to it. Sanitize all data before use.
Server-Side Request Forgery (SSRF) Payload contains a URL pointing to an internal service (e.g., http://169.254.169.254/). URL Whitelisting & Validation: If processing URLs, only allow requests to a predefined list of trusted domains. Never make requests to user-supplied arbitrary URLs.
Denial of Service (DoS) The handler is flooded with valid or invalid requests, overwhelming the server. Asynchronous Processing & Rate Limiting: Immediately place valid requests onto a message queue and return a 202 Accepted response. This decouples ingestion from processing. Implement IP-based rate limiting.

By treating every callback and webhook as a potentially hostile entry point, you can design a robust defense that isolates your core AI systems from threats originating in the complex web of third-party integrations.