Khi bạn sử dụng tính năng URL gọi lại của CaptchaAI (pingback), máy chủ của bạn sẽ hiển thị điểm cuối HTTP nhận giải pháp CAPTCHA. Nếu không xác thực, bất kỳ ai phát hiện ra URL đó đều có thể gửi giải pháp giả mạo. Hướng dẫn này trình bày cách bảo mật các điểm cuối gọi lại.
Luồng gọi lại
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
Vấn đề: bước 3 là một yêu cầu chưa được xác thực. Bạn cần xác minh nó thực sự đến từ CaptchaAI.
Chiến lược xác thực 1: Xác minh ID nhiệm vụ
Cách tiếp cận đơn giản nhất - chỉ chấp nhận kết quả gọi lại cho ID nhiệm vụ mà bạn thực sự đã gửi.
Python (Bình)
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);
Chiến lược xác thực 2: Mã thông báo chữ ký HMAC
Thêm mã thông báo bí mật vào URL gọi lại của bạn mà kẻ tấn công không thể đoán được.
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);
});
Sử dụng URL được tạo khi gửi: pingback=https://your-server.com/captcha/callback?token=HMAC_SIGNATURE.
Chiến lược xác thực 3: Danh sách cho phép IP
Hạn chế điểm cuối gọi lại của bạn ở IP máy chủ của CaptchaAI.
Python (Bình)
# 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();
});
Lưu ý: Hãy liên hệ với bộ phận hỗ trợ của CaptchaAI để biết danh sách IP nguồn gọi lại hiện tại. Nếu bạn sử dụng proxy ngược, hãy đảm bảo tiêu đề
X-Forwarded-Forđược định cấu hình chính xác.
Phòng chống tấn công phát lại
Ngay cả những cuộc gọi lại hợp lệ cũng có thể được phát lại. Thêm kiểm tra dấu thời gian và thực thi sử dụng một lần:
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
Danh sách kiểm tra bảo mật kết hợp
| Lớp | Bảo vệ chống lại | Thực hiện |
|---|---|---|
| Xác minh ID nhiệm vụ | Chèn nhiệm vụ ngẫu nhiên/unknown | Lưu trữ các ID đang chờ xử lý, từ chối các ID không xác định |
| chữ ký HMAC | Đoán URL, gọi lại giả mạo | Ký URL gọi lại bí mật |
| Danh sách cho phép IP | Yêu cầu từ máy chủ trái phép | Danh sách trắng IP CaptchaAI |
| Ngăn chặn phát lại | Đã gửi lại các lệnh gọi lại hợp lệ | Sử dụng một lần + xác thực dấu thời gian |
| HTTPS | Nghe lén, xen vào giữa | TLS trên điểm cuối gọi lại |
Khắc phục sự cố
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| Tất cả các cuộc gọi lại bị từ chối | Danh sách IP cho phép không bao gồm IP CaptchaAI | Xác minh IP hiện tại với sự hỗ trợ; kiểm tra tiêu đề proxy ngược |
| Xác minh HMAC không thành công | ID nhiệm vụ không khớp giữa gửi và gọi lại | Đảm bảo bạn sử dụng ID tác vụ chính xác được in.php trả về |
| Đã xử lý các lệnh gọi lại trùng lặp | Điều kiện cuộc đua trên các lệnh gọi lại đồng thời | Sử dụng các phép toán tập hợp nguyên tử hoặc các ràng buộc duy nhất của cơ sở dữ liệu |
| Hết thời gian gọi lại | Điểm cuối mất quá nhiều thời gian để phản hồi | Xử lý không đồng bộ - chấp nhận ngay lập tức, xử lý ở chế độ nền |
Câu hỏi thường gặp
Tôi có nên sử dụng cả bốn chiến lược xác thực cùng nhau không?
Sử dụng xác minh ID nhiệm vụ (Chiến lược 1) ở mức tối thiểu. Thêm chữ ký HMAC (Chiến lược 2) cho các điểm cuối công khai. Danh sách IP cho phép (Chiến lược 3) là lý tưởng nếu CaptchaAI xuất bản IP gọi lại ổn định. Ngăn ngừa lặp lại là điều cần thiết cho quy trình công việc tài chính hoặc nhạy cảm.
Điều gì xảy ra nếu điểm cuối gọi lại của tôi không hoạt động khi CaptchaAI gửi kết quả?
Giải pháp vẫn có sẵn thông qua điểm cuối bỏ phiếu (res.php). Triển khai một phương án dự phòng thăm dò ý kiến cho bất kỳ tác vụ nào không nhận được lệnh gọi lại trong một khoảng thời gian chờ.
Tôi có thể sử dụng TLS chung (mTLS) để xác thực gọi lại không?
Về lý thuyết là có – nhưng hệ thống gọi lại của CaptchaAI sử dụng các yêu cầu HTTPS GET tiêu chuẩn. Chữ ký HMAC cung cấp xác thực tương đương mà không yêu cầu quản lý chứng chỉ.
bài viết liên quan
Các bước tiếp theo
Bảo mật điểm cuối gọi lại CaptchaAI của bạn —lấy khóa API của bạnvà thực hiện xác nhận chữ ký.
Hướng dẫn liên quan: