عند استخدام ميزة عنوان URL لرد الاتصال الخاصة بـ CaptchaAI (pingback)، يكشف الخادم الخاص بك عن نقطة نهاية HTTP التي تتلقى حلول CAPTCHA. بدون التحقق من الصحة، يمكن لأي شخص يكتشف عنوان URL هذا إرسال حلول مزيفة. يغطي هذا البرنامج التعليمي كيفية تأمين نقاط نهاية رد الاتصال.
تدفق رد الاتصال
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
المشكلة: الخطوة 3 هي طلب لم تتم مصادقته. تحتاج إلى التحقق من أنه جاء بالفعل من CaptchaAI.
استراتيجية التحقق 1: التحقق من معرف المهمة
الطريقة الأبسط هي قبول نتائج رد الاتصال لمعرفات المهام التي أرسلتها بالفعل فقط.
بايثون (قارورة)
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 (اكسبرس)
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);
استراتيجية التحقق 2: رمز توقيع HMAC
أضف رمزًا سريًا إلى عنوان URL لرد الاتصال الخاص بك والذي لا يستطيع المهاجمون تخمينه.
بايثون
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);
});
استخدم عنوان URL الذي تم إنشاؤه عند الإرسال: pingback=https://your-server.com/captcha/callback?token=HMAC_SIGNATURE.
استراتيجية التحقق 3: القائمة المسموح بها لعنوان IP
قم بتقييد نقطة نهاية رد الاتصال الخاصة بك على عناوين IP لخادم CaptchaAI.
بايثون (قارورة)
# 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 (اكسبرس)
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();
});
ملاحظة: اتصل بدعم CaptchaAI للحصول على القائمة الحالية لعناوين IP لمصدر رد الاتصال. إذا كنت تستخدم وكيلًا عكسيًا، فتأكد من تكوين رؤوس
X-Forwarded-Forبشكل صحيح.
إعادة منع الهجوم
حتى عمليات الاسترجاعات الصالحة يمكن إعادة تشغيلها. أضف فحص الطابع الزمني وفرض الاستخدام لمرة واحدة:
بايثون
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
قائمة التحقق الأمنية المجمعة
| طبقة | الحماية ضد | التنفيذ |
|---|---|---|
| التحقق من معرف المهمة | Random/unknown حقن المهام | تخزين المعرفات المعلقة، ورفض المجهولين |
| توقيع HMAC | تخمين عنوان URL، وعمليات الاسترجاعات المزورة | قم بتسجيل عنوان URL لرد الاتصال بالسر |
| القائمة المسموح بها لعناوين IP | الطلبات من خوادم غير مصرح بها | القائمة البيضاء لعناوين IP CaptchaAI |
| منع إعادة التشغيل | تمت إعادة إرسال عمليات الاسترجاعات الصالحة | الاستخدام لمرة واحدة + التحقق من صحة الطابع الزمني |
| HTTPS | التنصت، أيها الرجل في المنتصف | TLS على نقطة نهاية رد الاتصال |
استكشاف الأخطاء وإصلاحها
| المشكلة | السبب | الإجراء |
|---|---|---|
| يُنشأ الرمز المميز لكن الجهة المستهدفة ترفضه | مفتاح الموقع أو الصفحة أو سياق الجلسة لا يتطابق | التقط المعلمات من جديد وأعد استخدام الرمز داخل جلسة HTTP أو المتصفح نفسها |
| تنتهي عملية الاستطلاع بمهلة | الفاصل الزمني أو وقت الانتظار أو معالجة الأخطاء صارمة أكثر من اللازم | استطلع كل 5 إلى 10 ثوانٍ وافصل بين انتهاء المهلة والأخطاء الفعلية وسجّل السبب |
| ينجح المثال محليًا لكنه يفشل داخل سير العمل | رد النداء أو حقل النموذج أو حقن الرمز مفقود في السلسلة الفعلية | تحقق من المسار الكامل بين أداة الحل والطلب النهائي إلى الموقع المستهدف |
الخطوات التالية
- البدء السريع مع CaptchaAI: حلّ أول كابتشا في 5 دقائق
- كيفية حلّ reCAPTCHA v2 عبر الـ API: دليل خطوة بخطوة
- كيفية حل Cloudflare Turnstile باستخدام واجهة API
- كيفية حل GeeTest v3 باستخدام API