Tutorials

CaptchaAI Webhook Security: Validating Callback Signatures

When you use CaptchaAI's callback URL feature (pingback), your server exposes an HTTP endpoint that receives CAPTCHA solutions. Without validation, anyone who discovers that URL can send fake solutions. This tutorial covers how to secure callback endpoints.

The Callback Flow


1. You submit task:
   POST https://ocr.captchaai.com/in.php
     ?key=YOUR_API_KEY
     &method=userrecaptcha
     &googlekey=SITE_KEY
     &pageurl=https://example.com
     &pingback=https://your-server.com/captcha/callback

2. CaptchaAI solves the CAPTCHA

3. CaptchaAI sends result to your endpoint:
   GET https://your-server.com/captcha/callback?id=TASK_ID&code=SOLUTION_TOKEN

The problem: step 3 is an unauthenticated request. You need to verify it actually came from CaptchaAI.

Validation Strategy 1: Task ID Verification

The simplest approach — only accept callback results for task IDs you actually submitted.

Python (Flask)

import os
import threading
import requests
from flask import Flask, request, jsonify

app = Flask(__name__)

# Thread-safe set of pending task IDs
pending_tasks = set()
pending_lock = threading.Lock()
results = {}

API_KEY = os.environ["CAPTCHAAI_API_KEY"]


def submit_captcha(sitekey, pageurl):
    """Submit CAPTCHA and register the task ID."""
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "pingback": "https://your-server.com/captcha/callback",
        "json": 1
    })
    data = resp.json()

    if data.get("status") == 1:
        task_id = data["request"]
        with pending_lock:
            pending_tasks.add(task_id)
        return task_id
    return None


@app.route("/captcha/callback")
def captcha_callback():
    task_id = request.args.get("id")
    solution = request.args.get("code")

    # Validate: only accept known task IDs
    with pending_lock:
        if task_id not in pending_tasks:
            return jsonify({"error": "unknown task"}), 403
        pending_tasks.discard(task_id)

    results[task_id] = solution
    return "OK", 200

JavaScript (Express)

const express = require("express");
const axios = require("axios");

const app = express();
const API_KEY = process.env.CAPTCHAAI_API_KEY;

const pendingTasks = new Set();
const results = new Map();

async function submitCaptcha(sitekey, pageurl) {
  const resp = await axios.post("https://ocr.captchaai.com/in.php", null, {
    params: {
      key: API_KEY,
      method: "userrecaptcha",
      googlekey: sitekey,
      pageurl: pageurl,
      pingback: "https://your-server.com/captcha/callback",
      json: 1,
    },
  });

  if (resp.data.status === 1) {
    const taskId = resp.data.request;
    pendingTasks.add(taskId);
    return taskId;
  }
  return null;
}

app.get("/captcha/callback", (req, res) => {
  const taskId = req.query.id;
  const solution = req.query.code;

  // Validate: only accept known task IDs
  if (!pendingTasks.has(taskId)) {
    return res.status(403).json({ error: "unknown task" });
  }

  pendingTasks.delete(taskId);
  results.set(taskId, solution);
  res.sendStatus(200);
});

app.listen(3000);

Validation Strategy 2: HMAC Signature Token

Add a secret token to your callback URL that attackers can't guess.

Python

import hashlib
import hmac
import os

CALLBACK_SECRET = os.environ["CALLBACK_SECRET"]  # Random 32+ character string


def generate_callback_url(task_id):
    """Generate callback URL with HMAC signature."""
    signature = hmac.new(
        CALLBACK_SECRET.encode(),
        task_id.encode(),
        hashlib.sha256
    ).hexdigest()

    return f"https://your-server.com/captcha/callback?token={signature}"


@app.route("/captcha/callback")
def captcha_callback():
    task_id = request.args.get("id")
    token = request.args.get("token")
    solution = request.args.get("code")

    # Verify HMAC signature
    expected = hmac.new(
        CALLBACK_SECRET.encode(),
        task_id.encode(),
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(token, expected):
        return jsonify({"error": "invalid signature"}), 403

    results[task_id] = solution
    return "OK", 200

JavaScript

const crypto = require("crypto");

const CALLBACK_SECRET = process.env.CALLBACK_SECRET;

function generateCallbackUrl(taskId) {
  const signature = crypto
    .createHmac("sha256", CALLBACK_SECRET)
    .update(taskId)
    .digest("hex");

  return `https://your-server.com/captcha/callback?token=${signature}`;
}

app.get("/captcha/callback", (req, res) => {
  const taskId = req.query.id;
  const token = req.query.token;
  const solution = req.query.code;

  // Verify HMAC signature
  const expected = crypto
    .createHmac("sha256", CALLBACK_SECRET)
    .update(taskId)
    .digest("hex");

  if (!crypto.timingSafeEqual(Buffer.from(token), Buffer.from(expected))) {
    return res.status(403).json({ error: "invalid signature" });
  }

  results.set(taskId, solution);
  res.sendStatus(200);
});

Use the generated URL when submitting: pingback=https://your-server.com/captcha/callback?token=HMAC_SIGNATURE.

Validation Strategy 3: IP Allowlisting

Restrict your callback endpoint to CaptchaAI's server IPs.

Python (Flask)

# CaptchaAI callback source IPs (verify current IPs with CaptchaAI support)
ALLOWED_IPS = {"138.201.XX.XX", "148.251.XX.XX"}  # Replace with actual IPs


@app.before_request
def check_ip():
    if request.path.startswith("/captcha/callback"):
        client_ip = request.remote_addr
        if client_ip not in ALLOWED_IPS:
            return jsonify({"error": "forbidden"}), 403

JavaScript (Express)

const ALLOWED_IPS = new Set(["138.201.XX.XX", "148.251.XX.XX"]);

app.use("/captcha/callback", (req, res, next) => {
  const clientIp = req.ip || req.connection.remoteAddress;
  if (!ALLOWED_IPS.has(clientIp)) {
    return res.status(403).json({ error: "forbidden" });
  }
  next();
});

Note: Contact CaptchaAI support for the current list of callback source IPs. If you're behind a reverse proxy, ensure X-Forwarded-For headers are configured correctly.

Replay Attack Prevention

Even valid callbacks can be replayed. Add a timestamp check and one-time-use enforcement:

Python

import time

CALLBACK_TTL = 300  # Reject callbacks older than 5 minutes
used_callbacks = set()


@app.route("/captcha/callback")
def captcha_callback():
    task_id = request.args.get("id")
    timestamp = request.args.get("ts")
    solution = request.args.get("code")

    # Check timestamp freshness
    if timestamp:
        age = time.time() - float(timestamp)
        if age > CALLBACK_TTL or age < 0:
            return jsonify({"error": "expired"}), 403

    # One-time use
    if task_id in used_callbacks:
        return jsonify({"error": "already processed"}), 409

    used_callbacks.add(task_id)
    results[task_id] = solution
    return "OK", 200

Combined Security Checklist

Layer Protection Against Implementation
Task ID verification Random/unknown task injection Store pending IDs, reject unknowns
HMAC signature URL guessing, forged callbacks Sign callback URL with secret
IP allowlisting Requests from unauthorized servers Whitelist CaptchaAI IPs
Replay prevention Resubmitted valid callbacks One-time-use + timestamp validation
HTTPS Eavesdropping, man-in-the-middle TLS on callback endpoint

Troubleshooting

Issue Cause Fix
All callbacks rejected IP allowlist doesn't include CaptchaAI IPs Verify current IPs with support; check reverse proxy headers
HMAC verification fails Task ID mismatch between submit and callback Ensure you use the exact task ID returned by in.php
Duplicate callbacks processed Race condition on concurrent callbacks Use atomic set operations or database unique constraints
Callbacks timing out Endpoint takes too long to respond Process async — accept immediately, process in background

FAQ

Should I use all four validation strategies together?

Use task ID verification (Strategy 1) as the minimum. Add HMAC signatures (Strategy 2) for public-facing endpoints. IP allowlisting (Strategy 3) is ideal if CaptchaAI publishes stable callback IPs. Replay prevention is essential for financial or sensitive workflows.

What happens if my callback endpoint is down when CaptchaAI sends the result?

The solution is still available via the polling endpoint (res.php). Implement a fallback that polls for any tasks that don't receive callbacks within a timeout period.

Can I use mutual TLS (mTLS) for callback authentication?

In theory, yes — but CaptchaAI's callback system uses standard HTTPS GET requests. HMAC signatures provide equivalent authentication without requiring certificate management.

Next Steps

Secure your CaptchaAI callback endpoints — get your API key and implement signature validation.

Related guides:

Discussions (0)

No comments yet.

Related Posts

Tutorials Webhook Endpoint Monitoring for CAPTCHA Solve Callbacks
Monitor your Captcha AI callback endpoints — track uptime, response latency, error rates, and set up alerts before missed results impact your pipeline.

Monitor your Captcha AI callback endpoints — track uptime, response latency, error rates, and set up alerts be...

Python Automation All CAPTCHA Types
Mar 12, 2026
API Tutorials CaptchaAI Callback URL Setup: Complete Webhook Guide
Set up Captcha AI callback URLs to receive solved CAPTCHA tokens via webhook instead of polling.

Set up Captcha AI callback URLs to receive solved CAPTCHA tokens via webhook instead of polling. Python Flask...

Python Automation All CAPTCHA Types
Feb 06, 2026
Tutorials CaptchaAI Callback URL Error Handling: Retry and Dead-Letter Patterns
Handle Captcha AI callback failures gracefully — implement retry logic, dead-letter queues, and fallback polling for missed CAPTCHA results.

Handle Captcha AI callback failures gracefully — implement retry logic, dead-letter queues, and fallback polli...

Python Automation All CAPTCHA Types
Feb 01, 2026
API Tutorials CaptchaAI IP Whitelisting and API Key Security
Secure your Captcha AI API key with IP whitelisting, environment variables, and access control best practices.

Secure your Captcha AI API key with IP whitelisting, environment variables, and access control best practices.

Python Automation All CAPTCHA Types
Jan 30, 2026
API Tutorials CaptchaAI Pingback and Task Notification Patterns
Implement advanced pingback (callback) patterns for Captcha AI.

Implement advanced pingback (callback) patterns for Captcha AI. Learn fire-and-forget, multi-task fan-out, web...

Python Automation All CAPTCHA Types
Mar 16, 2026
Tutorials Discord Webhook Alerts for CAPTCHA Pipeline Status
Send CAPTCHA pipeline alerts to Discord — webhook integration for balance warnings, error spikes, queue status, and daily summary reports with Captcha AI.

Send CAPTCHA pipeline alerts to Discord — webhook integration for balance warnings, error spikes, queue status...

Python Automation All CAPTCHA Types
DevOps & Scaling Blue-Green Deployment for CAPTCHA Solving Infrastructure
Implement blue-green deployments for CAPTCHA solving infrastructure — zero-downtime upgrades, traffic switching, and rollback strategies with Captcha AI.

Implement blue-green deployments for CAPTCHA solving infrastructure — zero-downtime upgrades, traffic switchin...

Python Automation All CAPTCHA Types
Apr 07, 2026
DevOps & Scaling Ansible Playbooks for CaptchaAI Worker Deployment
Deploy and manage Captcha AI workers with Ansible — playbooks for provisioning, configuration, rolling updates, and health checks across your server fleet.

Deploy and manage Captcha AI workers with Ansible — playbooks for provisioning, configuration, rolling updates...

Python Automation All CAPTCHA Types
Apr 07, 2026
Tutorials Streaming Batch Results: Processing CAPTCHA Solutions as They Arrive
Process CAPTCHA solutions the moment they arrive instead of waiting for tasks to complete — use async generators, event emitters, and callback patterns for stre...

Process CAPTCHA solutions the moment they arrive instead of waiting for all tasks to complete — use async gene...

Python Automation All CAPTCHA Types
Apr 07, 2026
Tutorials Handling Multiple CAPTCHAs on a Single Page
how to detect and solve multiple CAPTCHAs on a single web page using Captcha AI.

Learn how to detect and solve multiple CAPTCHAs on a single web page using Captcha AI. Covers multi-iframe ext...

Python Cloudflare Turnstile reCAPTCHA v2
Apr 09, 2026
Tutorials Pytest Fixtures for CaptchaAI API Testing
Build reusable pytest fixtures to test CAPTCHA-solving workflows with Captcha AI.

Build reusable pytest fixtures to test CAPTCHA-solving workflows with Captcha AI. Covers mocking, live integra...

Python Automation Cloudflare Turnstile
Apr 08, 2026