Eğitimler

CaptchaAI Webhook Güvenliği: Geri Arama İmzalarını Doğrulama

CaptchaAI'nin geri arama URL'si özelliğini (pingback) kullandığınızda, sunucunuz CAPTCHA çözümleri alan bir HTTP uç noktasını kullanıma sunar. Doğrulama olmadan bu URL'yi keşfeden herkes sahte çözümler gönderebilir. Bu eğitimde geri arama uç noktalarının güvenliğinin nasıl sağlanacağı anlatılmaktadır.

Geri Arama Akışı


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

Sorun: 3. adım, kimliği doğrulanmamış bir istektir. Gerçekten CaptchaAI'den geldiğini doğrulamanız gerekiyor.

Doğrulama Stratejisi 1: Görev Kimliği Doğrulaması

En basit yaklaşım; yalnızca gerçekten gönderdiğiniz görev kimliklerinin geri arama sonuçlarını kabul etmektir.

Python (Şişe)

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 (Ekspres)

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);

Doğrulama Stratejisi 2: HMAC İmza Tokenı

Geri arama URL'nize saldırganların tahmin edemeyeceği gizli bir belirteç ekleyin.

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);
});

Gönderirken oluşturulan URL'yi kullanın: pingback=https://your-server.com/captcha/callback?token=HMAC_SIGNATURE.

Doğrulama Stratejisi 3: IP İzin Verilenler Listesine Ekleme

Geri arama uç noktanızı CaptchaAI'nin sunucu IP'leriyle sınırlandırın.

Python (Şişe)

# 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 (Ekspres)

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();
});

Not: Geri arama kaynağı IP'lerinin geçerli listesi için CaptchaAI desteğine başvurun. Ters proxy arkasındaysanız X-Forwarded-For üstbilgilerinin doğru şekilde yapılandırıldığından emin olun.

Tekrar Saldırısını Önleme

Geçerli geri aramalar bile tekrar oynatılabilir. Zaman damgası kontrolü ve tek kullanımlık yaptırım ekleyin:

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

Birleşik Güvenlik Kontrol Listesi

Katman Karşı Koruma Uygulama
Görev Kimliği doğrulaması Rastgele/unknown görev enjeksiyonu Bekleyen kimlikleri saklayın, bilinmeyenleri reddedin
HMAC imzası URL tahmin etme, sahte geri aramalar Geri arama URL'sini gizli olarak imzala
IP'yi izin verilenler listesine ekleme Yetkisiz sunuculardan gelen istekler CaptchaAI IP'lerini beyaz listeye alın
Tekrar oynatmayı önleme Geçerli geri aramalar yeniden gönderildi Tek kullanımlık + zaman damgası doğrulaması
HTTPS Kulak misafiri, ortadaki adam Geri arama uç noktasında TLS

Sorun giderme

Sorun Sebep Düzeltme
Tüm geri aramalar reddedildi IP izin verilenler listesi CaptchaAI IP'lerini içermiyor Mevcut IP'leri destekle doğrulayın; ters proxy başlıklarını kontrol edin
HMAC doğrulaması başarısız oluyor Gönderme ve geri arama arasında görev kimliği uyuşmazlığı in.php tarafından döndürülen görev kimliğini tam olarak kullandığınızdan emin olun
Yinelenen geri aramalar işlendi Eşzamanlı geri aramalarda yarış durumu Atomik küme işlemlerini veya veritabanı benzersiz kısıtlamalarını kullanın
Geri aramalar zaman aşımına uğradı Uç noktanın yanıt vermesi çok uzun sürüyor Eşzamansız işle – hemen kabul et, arka planda işle

SSS

Dört doğrulama stratejisinin tümünü birlikte mi kullanmalıyım?

Minimum olarak görev kimliği doğrulamasını (Strateji 1) kullanın. Herkese açık uç noktalar için HMAC imzaları ekleyin (Strateji 2). CaptchaAI'nin kararlı geri çağırma IP'leri yayınlaması durumunda IP izin verilenler listesine ekleme (Strateji 3) idealdir. Tekrarın önlenmesi finansal veya hassas iş akışları için çok önemlidir.

CaptchaAI sonucu gönderdiğinde geri arama uç noktam kapalıysa ne olur?

Çözüm hâlâ yoklama uç noktası (res.php) aracılığıyla kullanılabilir. Bir zaman aşımı süresi içinde geri arama almayan tüm görevleri yoklayan bir geri dönüş uygulayın.

Geri arama kimlik doğrulaması için karşılıklı TLS (mTLS) kullanabilir miyim?

Teorik olarak evet - ancak CaptchaAI'nin geri arama sistemi standart HTTPS GET isteklerini kullanır.HMAC imzaları, sertifika yönetimi gerektirmeden eşdeğer kimlik doğrulama sağlar.

İlgili Makaleler

Sonraki Adımlar

CaptchaAI geri arama uç noktalarınızı güvence altına alın —API anahtarınızı alınve imza doğrulamasını uygulayın.

İlgili kılavuzlar:

Bu makale için yorumlar devre dışı bırakılmıştır.