Saat Anda menggunakan fitur URL callback CaptchaAI (pingback), server Anda mengekspos endpoint HTTP yang menerima solusi CAPTCHA. Tanpa validasi, siapa pun yang menemukan URL tersebut dapat mengirimkan solusi palsu. Tutorial ini membahas cara mengamankan endpoint callback.
Alur Callback
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
Masalahnya: langkah 3 adalah request yang tidak terautentikasi. Anda perlu memverifikasi bahwa itu benar-benar berasal dari CaptchaAI.
Strategi Validasi 1: Verifikasi Task ID
Pendekatan paling sederhana — hanya terima hasil callback untuk task ID yang benar-benar Anda submit.
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);
Strategi Validasi 2: HMAC Signature Token
Tambahkan token rahasia ke URL callback Anda yang tidak dapat ditebak penyerang.
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);
});
Gunakan URL yang dihasilkan saat submit: pingback=https://your-server.com/captcha/callback?token=HMAC_SIGNATURE.
Strategi Validasi 3: IP Allowlist
Batasi endpoint callback Anda ke IP server CaptchaAI.
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 (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();
});
Catatan: Hubungi dukungan CaptchaAI untuk mengetahui daftar IP sumber callback terkini. Jika Anda menggunakan reverse proxy, pastikan header
X-Forwarded-Fordikonfigurasi dengan benar.
Pencegahan Replay Attack
Bahkan callback yang valid bisa di-replay. Tambahkan pemeriksaan timestamp dan one-time enforcement:
Python = 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
```
Checklist Keamanan Gabungan
| Lapisan | Perlindungan Terhadap | Implementasi |
|---|---|---|
| Verifikasi Task ID | Task injeksi acak/tidak dikenal | Simpan ID pending, tolak yang tidak dikenal |
| HMAC signature | Menebak URL, callback palsu | Tanda tangani URL callback dengan secret |
| IP allowlist | Request dari server tidak sah | Whitelist IP CaptchaAI |
| Pencegahan replay | Mengirim ulang callback valid | Validasi timestamp + one-time use |
| HTTPS | Penyadapan, man-in-the-middle | TLS pada endpoint callback |
Pemecahan Masalah
| Masalah | Penyebab | Solusi |
|---|---|---|
| Semua callback ditolak | IP allowlist tidak mencakup IP CaptchaAI | Verifikasi IP terkini dengan support; periksa header reverse proxy |
| Verifikasi HMAC gagal | Task ID tidak cocok antara submit dan callback | Pastikan menggunakan task ID persis yang dikembalikan oleh in.php |
| Callback duplikat diproses | Race condition pada callback bersamaan | Gunakan operasi set atomik atau unique constraint database |
| Callback timeout | Endpoint butuh terlalu lama merespons | Proses async — segera terima, proses di background |
Pertanyaan Umum
Haruskah saya menggunakan keempat strategi validasi sekaligus?
Gunakan verifikasi task ID (Strategi 1) sebagai minimum. Tambahkan HMAC signature (Strategi 2) untuk endpoint yang dapat diakses publik. IP allowlist (Strategi 3) ideal jika CaptchaAI menerbitkan IP callback yang stabil. Pencegahan replay sangat penting untuk workflow finansial atau sensitif.
Apa yang terjadi jika endpoint callback saya mati saat CaptchaAI mengirim hasilnya?
Solusi masih tersedia melalui endpoint polling (res.php). Implementasikan fallback yang melakukan polling untuk task apa pun yang tidak menerima callback dalam periode timeout.
Bisakah saya menggunakan mutual TLS (mTLS) untuk autentikasi callback?
Secara teori ya — tetapi sistem callback CaptchaAI menggunakan request HTTPS GET standar. HMAC signature memberikan autentikasi setara tanpa memerlukan manajemen sertifikat.
Artikel Terkait
- Pola Penanganan Error Callback CaptchaAI
- Panduan Webhook URL Callback CaptchaAI
- Pola Notifikasi Task Pingback