Kısa yanıt: bir callback uç noktasını tek bir kontrolle değil, üst üste binen dört katmanla güvenceye alırsınız — görev kimliği (task ID) doğrulaması, HMAC imzası, IP izin listesi ve replay koruması. CaptchaAI'nin pingback özelliğini kullandığınızda sunucunuz, CAPTCHA çözümlerini karşılayan herkese açık bir HTTP uç noktası yayınlar. Türkiye'deki e-ticaret ve fintech ekiplerinde bu uç nokta çoğu zaman ödeme veya giriş akışlarının QA testlerinde tetiklenir; doğrulama olmadan bıraktığınızda ise URL'yi ele geçiren biri sahte çözümler enjekte edebilir ve KVKK kapsamındaki verileri riske atabilir. Doğrulamasız bir uç noktada karşılaştığınız somut riskler şunlardır:
- Sahte çözüm enjeksiyonu: URL'yi keşfeden biri, uç noktanızı geçersiz token'larla doldurabilir.
- Yanlış görev eşleştirmesi: hiç göndermediğiniz task ID'lere ait sonuçlar veri tablonuzu kirletir.
- Tekrar (replay): ağ üzerinde yakalanan geçerli bir callback yeniden gönderilerek çift işlem yaratır.
Callback akışı nasıl çalışır?
Tipik akış üç adımdan oluşur:
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
Zayıf nokta 3. adımdır: bu istek kimliği doğrulanmamıştır. İsteğin gerçekten CaptchaAI'den mi, yoksa uç noktanızı tarayan birinden mi geldiğini ayırt etmeniz gerekir. Aşağıdaki dört katman tam olarak bunu sağlar.
Dört savunma katmanına genel bakış
- Görev kimliği doğrulaması — yalnızca sizin gönderdiğiniz task ID'lere ait callback'leri kabul edin.
- HMAC imzası — callback URL'sini gizli bir anahtarla imzalayıp uç noktada doğrulayın.
- IP izin listesi — yalnızca CaptchaAI'nin sunucu IP'lerinden gelen isteklere izin verin.
- Replay koruması — zaman damgası ve tek kullanımlık yaptırımla tekrar gönderimi engelleyin.
Strateji 1: Görev kimliği (task ID) doğrulaması
En düşük maliyetli katmanla başlayın: gönderdiğiniz her task ID'yi bir kümede tutun, callback geldiğinde ID kümede yoksa 403 döndürün.
Python (Flask)
Aşağıdaki Flask uygulaması, gönderdiği her task ID'yi bir kümede tutar ve callback'te bu kimliği doğrular:
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)
Aynı mantığın Express karşılığı, bekleyen ID'leri bir Set içinde tutar:
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);
Bu katman şunları durdurur:
- Rastgele veya tahmin edilen task ID'lerle yapılan enjeksiyonu.
- Hiç göndermediğiniz görevlere ait sahte sonuçları.
- Sınır: URL'yi ve geçerli bir ID'yi birlikte ele geçiren saldırganı tek başına durduramaz — bunun için Strateji 2'yi ekleyin.
Strateji 2: HMAC imza token'ı
Task ID doğrulaması rastgele denemeleri eler, ama URL'yi gören biri geçerli bir ID'yi de görmüş olabilir. Bu katman, callback URL'sine tahmin edilemez bir imza ekler: gizli bir anahtarla task ID'yi imzalar, uç noktada aynı imzayı yeniden hesaplayıp sabit zamanlı karşılaştırmayla doğrularsınız.
Python
Aşağıdaki fonksiyon imzalı URL üretir; callback tarafında imza yeniden hesaplanıp hmac.compare_digest ile karşılaştırılır:
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
Node.js tarafında crypto modülüyle timingSafeEqual üzerinden aynı doğrulama:
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);
});
İmza katmanını doğru işletmek için:
- Görevi gönderirken imzalı URL'yi kullanın:
pingback=https://your-server.com/captcha/callback?token=HMAC_SIGNATURE. - Gizli anahtarı koda gömmeyin; ortam değişkeninde saklayın.
- Anahtar en az 32 karakter rastgele olsun ve sızdığından şüphelenirseniz hemen döndürün.
Strateji 3: IP izin listesi (allowlist)
Üçüncü katman ağ seviyesinde çalışır: callback uç noktanızı yalnızca CaptchaAI'nin sunucu IP'lerinden gelen isteklere açın. Bu, geçerli bir imza olmadan gelen trafiği daha uç noktaya varmadan reddeder.
Python (Flask)
Flask'ta bir before_request kancasıyla istemci IP'sini filtreleyin:
# 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)
Express'te aynı filtre bir ara katman (middleware) olarak eklenir:
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: Güncel callback kaynak IP'leri listesi için CaptchaAI desteğine başvurun. Ters proxy arkasındaysanız
X-Forwarded-Forbaşlıklarının doğru yapılandırıldığından emin olun — aksi halde gerçek istemci IP'si yerine proxy'nin IP'sini kontrol edersiniz.
Bu katmanı ne zaman eklemelisiniz:
- CaptchaAI kararlı callback IP'leri yayınladığında en etkilidir.
- Tek başına kimlik doğrulama değil, HMAC imzasını tamamlayan bir ağ katmanıdır.
Tekrar (replay) saldırılarını önleme
Geçerli bir callback bile ağ üzerinde yakalanıp yeniden gönderilebilir. Özellikle ödeme onayı gibi finansal iş akışlarında — Türkiye'deki e-ticaret checkout QA senaryolarında sık karşılaşılır — aynı çözümün iki kez işlenmesi çift kayda yol açar. İki basit kontrol bunu engeller:
- Zaman damgası (
ts) tazeliğini kontrol edin: belirli bir süreden eski callback'leri reddedin. - Her task ID'yi yalnızca bir kez işleyin; ikinci kez gelirse
409döndürün.
Aşağıdaki uç nokta her iki kontrolü de uygular:
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
Katmanlı güvenlik kontrol listesi
Dört katmanı birlikte uyguladığınızda savunma şöyle görünür:
| Katman | Neye karşı korur | Uygulama |
|---|---|---|
| Görev kimliği doğrulaması | Rastgele/bilinmeyen görev enjeksiyonu | Bekleyen ID'leri saklayın, bilinmeyeni reddedin |
| HMAC imzası | URL tahmini, sahte callback'ler | Callback URL'sini gizli anahtarla imzalayın |
| IP izin listesi | Yetkisiz sunuculardan gelen istekler | Yalnızca CaptchaAI IP'lerine izin verin |
| Replay koruması | Yeniden gönderilen geçerli callback'ler | Tek kullanım + zaman damgası doğrulaması |
| HTTPS | Dinleme, ortadaki adam (MITM) | Callback uç noktasında TLS |
Sorun giderme
| Sorun | Sebep | Düzeltme |
|---|---|---|
| Tüm callback'ler reddediliyor | IP izin listesi CaptchaAI IP'lerini içermiyor | Güncel IP'leri destekle doğrulayın; ters proxy başlıklarını kontrol edin |
| HMAC doğrulaması başarısız oluyor | Gönderim ve callback arasında task ID uyuşmazlığı | in.php'nin döndürdüğü task ID'yi birebir kullanın |
| Yinelenen callback'ler işleniyor | Eşzamanlı callback'lerde yarış durumu | Atomik küme işlemleri veya veritabanı benzersizlik kısıtı kullanın |
| Callback'ler zaman aşımına uğruyor | Uç nokta yanıtı çok geç veriyor | Eşzamansız işleyin — hemen kabul edin, arka planda işleyin |
Sık sorulan sorular
Callback güvenliğini uygularken en sık karşılaşılan sorular:
HMAC gizli anahtarını nerede saklamalıyım?
Kod deposunda değil, ortam değişkeninde veya bir secret yöneticisinde tutun. Anahtar en az 32 karakter rastgele olmalı ve sızdığından şüphelenirseniz hemen döndürülmelidir; imza güvenliğinin tamamı bu anahtarın gizli kalmasına dayanır.
IP izin listesi tek başına yeterli mi?
Hayır. IP'ler zamanla değişebilir ve ters proxy arkasında X-Forwarded-For ile taklit edilebilir. IP izin listesini tamamlayıcı bir ağ katmanı olarak görün; birincil kimlik doğrulamayı HMAC imzasıyla yapın.
İmza doğrulaması callback yanıtını yavaşlatır mı?
Fark edilmeyecek kadar az. HMAC-SHA256 hesaplaması mikrosaniyeler sürer. Asıl gecikme çözümü senkron işlemekten gelir — callback'i hemen 200 ile kabul edip ağır işi arka plana alın.
Callback uç noktam yanıt vermezse çözümü kaybeder miyim?
Hayır. Çözüm res.php sorgulama uç noktasında hâlâ durur. Belirli bir süre içinde callback almayan görevleri periyodik olarak sorgulayan bir yedek mekanizma kurun.
İlgili Makaleler
Sonraki Adımlar
CaptchaAI callback uç noktalarınızı güvenceye alın — API anahtarınızı alın ve imza doğrulamasını bugün ekleyin. İlgili kılavuzlar: