接手一个已经部署 captcha 或其他防护的站点,却无法访问源代码?此时最关心的是使用了哪种方案、配置是否正确以及如何测试。
在本文中,我们尽量回答了所有关键问题。要开始解决问题,第一步是确定当前使用的是哪种防护系统。为此,您可以查看常见验证码与反机器人防护系统列表,其中提供了可视化示例和关键特征,帮助您快速判断自己正在使用哪一种方案。
如果您发现自己的网站使用的是 ComplexImage,下一步就是更深入地了解它的特性和具体工作方式。在本文中,您还可以查看 ComplexImage 的接入说明,以便彻底弄清它在您的网站上是如何运行的。这样一来,您不仅能更好地理解当前的防护机制,还可以更合理地规划后续的维护和支持。
什么是图像验证码
图像验证码是一种视觉 CAPTCHA 类型,要求用户执行与图像分析相关的任务。通常系统会要求选择符合特定条件的图片、计算物体数量、确定对象的正确位置或执行其他视觉操作。与文本验证码不同,文本验证码只需用户输入图片上的字符,而图像验证码会测试用户解释视觉信息和执行逻辑操作的能力。这使得自动识别更困难,并有效保护注册、登录以及网站的其他关键点免受机器人攻击。
通过 CapMonster Cloud 解决图像验证码
在测试带有腾讯 CAPTCHA 的表单时,通常需要验证 CAPTCHA 是否正常工作并正确集成。
您可以手动测试已添加到网站的验证码:
- 打开包含表单的页面,并确保 CAPTCHA 正常显示。
- 尝试在未完成 CAPTCHA 的情况下提交表单——服务器应返回错误。
- 成功解决 CAPTCHA 后,表单应无错误地提交。
要自动解决验证码,可以使用专用服务,例如 CapMonster Cloud —— 该工具接收验证码参数,在其服务器上处理,并返回完成的解决方案,例如数字或点击坐标,可用于无需用户参与的验证。
通过 CapMonster Cloud API 工作的一般流程:
此步骤提供 API key、CAPTCHA 类型及参数。服务返回唯一 taskId,可用于后续获取结果。
解决图像验证码的请求需要包含以下参数:
type - ComplexImageTask
class - recognition
imagesBase64 - base64 格式的图像数组。例如:[“/9j/4AAQSkZJRgABAQEAAAAAAAD…”];
Task(在 metadata 中) - 任务名称(例如 dli)。
关于参数以及如何自动收集,可参阅 CapMonster Cloud 文档。
提交任务的地址:
https://api.capmonster.cloud/createTask请求示例:
{
"clientKey": "API_KEY",
"task": {
"type": "ComplexImageTask",
"class": "recognition",
"imagesBase64": [
"base64"
],
"metadata": {
"Task": "dli" // 替换为所需任务,可在 https://docs.capmonster.cloud/zh/docs/captchas/ComplexImageTask-Recognition/ 找到可用模块列表
}
}
}
响应:
{
"errorId":0,
"taskId":407533072
}创建任务后,请定期查询解题状态。
接收结果的地址:
https://api.capmonster.cloud/getTaskResult请求示例:
{
"clientKey":"API_KEY",
"taskId": 407533072
}
响应:
{
"solution":
{
"answer": "1",
"metadata": {
"AnswerType": "Text"
}
},
"cost": 0.0003,
"status": "ready",
"errorId": 0,
"errorCode": null,
"errorDescription": null
}
获取的 token 可以插入到表单隐藏字段中,或调用网站上的 JS 函数以确认解决方案。之后服务器会将表单视为正确填写。为了自动化和测试,可以使用 Puppeteer、Selenium 或 Playwright,这些工具可以模拟用户操作、插入 token 并提交表单。
使用现成库识别图像验证码
CapMonster Cloud 服务提供适用于 Python、JavaScript (Node.js) 和 C# 的现成库。
Python
JavaScript
C#
填写答案并提交表单
Node.js 示例展示了在网页上完整的验证码识别流程。方法:使用 HTTP 请求获取 HTML 和防护系统参数,提交答案并处理结果。或者如下面示例,使用自动化工具(Playwright)——打开页面,等待验证,使用 CapMonster Cloud 提交参数,获取结果,将其填入对应字段(测试时可使用正确或错误数据),查看结果。
// npm install playwright @zennolab_com/capmonstercloud-client
// npx playwright install chromium
import { chromium } from 'playwright';
import { CapMonsterCloudClientFactory, ClientOptions, ComplexImageTaskRecognitionRequest } from '@zennolab_com/capmonstercloud-client';
const API_KEY = "YOUR_API_KEY";
const TARGET_URL = "https://example.com/captcha-page";
async function solveComplexImageTaskPlaywright() {
const browser = await chromium.launch({ headless: false });
const context = await browser.newContext();
const page = await context.newPage();
await page.goto(TARGET_URL);
// 找到 CAPTCHA 图像
const captchaHandle = await page.$('#captcha'); // 替换为真实选择器
const captchaBase64 = await captchaHandle.evaluate(img => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
return canvas.toDataURL('image/png').split(',')[1];
});
console.log("Captcha base64:", captchaBase64.substring(0, 50) + "...");
const cmcClient = CapMonsterCloudClientFactory.Create(
new ClientOptions({ clientKey: API_KEY })
);
// 提交 CAPTCHA 进行识别
const citRecognitionRequest = new ComplexImageTaskRecognitionRequest({
imagesBase64: [captchaBase64],
metaData: { Task: 'oocl_rotate' } // 替换为您的 CAPTCHA 类型
});
const result = await cmcClient.Solve(citRecognitionRequest);
console.log("Solution received:", result);
// 处理结果
const solution = result.solution;
if (!solution) {
console.error("No solution received");
return;
}
if (solution.metadata?.AnswerType === "Coordinate") {
// 带坐标的 CAPTCHA
const box = await captchaHandle.boundingBox();
for (const point of solution.answer) {
const clickX = box.x + point.X;
const clickY = box.y + point.Y;
console.log(`Clicking at: (${clickX}, ${clickY})`);
await page.mouse.click(clickX, clickY);
}
} else if (solution.metadata?.AnswerType === "Grid") {
// 网格 CAPTCHA(true/false 数组)
const box = await captchaHandle.boundingBox();
const gridItems = await page.$$('#captcha_grid div'); // 替换为网格元素选择器
const answers = solution.answer;
for (let i = 0; i < answers.length; i++) {
if (answers[i] && gridItems[i]) {
const itemBox = await gridItems[i].boundingBox();
const clickX = itemBox.x + itemBox.width / 2;
const clickY = itemBox.y + itemBox.height / 2;
console.log(`Clicking grid item ${i} at: (${clickX}, ${clickY})`);
await page.mouse.click(clickX, clickY);
}
}
} else {
console.warn("Unknown captcha solution type:", solution.metadata?.AnswerType);
}
// 点击确认按钮(如有)
await page.click('#submit_button'); // 替换为真实按钮选择器
console.log("Captcha solved.");
}
solveComplexImageTaskPlaywright().catch(console.error);
如何将图像验证码连接到您的网站
要清楚了解 CAPTCHA 在网站上的工作方式、验证逻辑或重新配置,我们建议您参考本节。它描述了保护措施的一般集成流程,帮助快速掌握所有细节。
1. 在服务器端生成 CAPTCHA。
- 生成单张图像或图像网格。
- 添加干扰、扭曲和随机元素以防机器人。
- 生成唯一 captchaId 并保存正确答案(在内存、数据库或缓存中)。
2. 将 CAPTCHA 传给客户端
- 服务器向客户端发送 captchaId 和图像(base64 或 URL)。
- 客户端显示 CAPTCHA,例如:<img> 或带指示的 <div> 网格。
如需从零创建 CAPTCHA,可参考 详细指南。
<!--图像网格 CAPTCHA-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Grid CAPTCHA Demo</title>
<style>
#captchaGrid {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-gap: 10px;
margin-bottom: 10px;
}
.grid-item {
width: 100px;
height: 100px;
background-color: #eee;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 2px solid transparent;
}
.grid-item.selected {
border-color: blue;
}
</style>
</head>
<body>
<h1>Grid CAPTCHA Demo</h1>
<div id="captchaGrid"></div>
<button id="submitBtn">Submit</button>
<button id="refreshBtn">Refresh CAPTCHA</button>
<p id="result"></p>
<script>
let captchaId;
let answers = []; // 点击结果为 true/false 数组
async function loadCaptcha() {
const res = await fetch('/captcha'); // 服务器应返回 JSON,包含 captchaId 和 base64 图像数组
const data = await res.json();
captchaId = data.captchaId;
answers = new Array(data.images.length).fill(false);
const grid = document.getElementById('captchaGrid');
grid.innerHTML = '';
data.images.forEach((imgBase64, i) => {
const div = document.createElement('div');
div.className = 'grid-item';
div.style.backgroundImage = `url('data:image/png;base64,${imgBase64}')`;
div.style.backgroundSize = 'cover';
div.addEventListener('click', () => {
answers[i] = !answers[i];
div.classList.toggle('selected', answers[i]);
});
grid.appendChild(div);
});
document.getElementById('result').textContent = '';
}
document.getElementById('refreshBtn').addEventListener('click', loadCaptcha);
document.getElementById('submitBtn').addEventListener('click', async () => {
const res = await fetch('/captcha/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ captchaId, answer: answers })
});
const data = await res.json();
document.getElementById('result').textContent = data.success ? 'Captcha passed!' : 'Captcha failed, try again.';
if (!data.success) loadCaptcha();
});
// 启动时加载 CAPTCHA
loadCaptcha();
</script>
</body>
</html>
3. 用户输入答案
- 用户输入文本、点击图像或旋转对象。
- 客户端生成适当结构的答案:
- 文本 CAPTCHA → 字符串
- 网格 → true/false 数组
- 坐标 → {X, Y} 数组
4. 将答案发送到服务器:
- 客户端 POST captchaId 和答案 (answer)。
- 可传递会话 token 防止重复提交。
5. 服务器端验证
- 服务器根据 captchaId 查找 CAPTCHA 会话。
- 将用户答案与正确答案对比。
- 返回 success: true/false。
- 失败时可刷新 CAPTCHA。
<?php
session_start();
// CAPTCHA TTL(秒)
define('CAPTCHA_TTL', 300); // 5 分钟
// 生成随机图像
function generateCaptchaImage($text = null) {
$width = 100;
$height = 100;
if (!$text) {
$text = substr(str_shuffle('ABCDEFGHJKLMNPQRSTUVWXYZ23456789'), 0, 2);
}
$image = imagecreatetruecolor($width, $height);
// 背景
$bgColor = imagecolorallocate($image, rand(180, 255), rand(180, 255), rand(180, 255));
imagefilledrectangle($image, 0, 0, $width, $height, $bgColor);
// 文本
$textColor = imagecolorallocate($image, 0, 0, 0);
$fontSize = 15;
$fontFile = __DIR__ . '/Arial.ttf'; // TTF 字体路径
if (file_exists($fontFile)) {
imagettftext($image, $fontSize, rand(-20,20), 10, 50, $textColor, $fontFile, $text);
} else {
imagestring($image, 5, 10, 40, $text, $textColor);
}
ob_start();
imagepng($image);
$imgData = ob_get_clean();
imagedestroy($image);
return base64_encode($imgData);
}
// 创建网格 CAPTCHA
function generateGridCaptcha() {
$numImages = 9; // 3x3
$images = [];
$solution = [];
for ($i = 0; $i < $numImages; $i++) {
// 随机解决,正确或错误(示例)
$isCorrect = rand(0,1) === 1;
$solution[] = $isCorrect;
// 生成图像(可添加不同对象以生成真实 CAPTCHA)
$text = $isCorrect ? 'OK' : 'NO';
$images[] = generateCaptchaImage($text);
}
return ['images' => $images, 'solution' => $solution];
}
// 生成 CAPTCHA 接口
if ($_SERVER['REQUEST_METHOD'] === 'GET' && $_SERVER['REQUEST_URI'] === '/captcha') {
$captchaId = uniqid('captcha_', true);
$gridCaptcha = generateGridCaptcha();
$_SESSION['captchas'][$captchaId] = [
'solution' => $gridCaptcha['solution'],
'timestamp' => time()
];
header('Content-Type: application/json');
echo json_encode([
'captchaId' => $captchaId,
'images' => $gridCaptcha['images']
]);
exit;
}
// 验证 CAPTCHA 接口
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $_SERVER['REQUEST_URI'] === '/captcha/verify') {
$data = json_decode(file_get_contents('php://input'), true);
$captchaId = $data['captchaId'] ?? '';
$answer = $data['answer'] ?? [];
if (!isset($_SESSION['captchas'][$captchaId])) {
echo json_encode(['success' => false, 'message' => 'Captcha expired or not found']);
exit;
}
$captcha = $_SESSION['captchas'][$captchaId];
// TTL 验证
if (time() - $captcha['timestamp'] > CAPTCHA_TTL) {
unset($_SESSION['captchas'][$captchaId]);
echo json_encode(['success' => false, 'message' => 'Captcha expired']);
exit;
}
// true/false 数组验证
$success = $captcha['solution'] === $answer;
// 验证后删除 CAPTCHA
unset($_SESSION['captchas'][$captchaId]);
echo json_encode(['success' => $success]);
exit;
}
// 404
http_response_code(404);
echo 'Not found';
6. 后续操作
- 成功 CAPTCHA → 允许受保护流程(表单、注册等)。
- 失败 CAPTCHA → 生成新 CAPTCHA,可限制尝试次数。
附加
- 使用 TTL(存活时间)存储 CAPTCHA 会话,实现自动过期。
- 缓存图像并使用临时 URL 节约资源。
- 确保移动设备上点击和触控功能正常。
- 记录日志和分析,以改善用户体验和防护效果。
可能的错误与调试
图像验证码未加载
(空白网格、损坏的图片、404/500 错误、base64 问题) — 请检查服务器是否正确生成图片、base64 数据未损坏、图片格式被浏览器支持,并且客户端收到了有效的 captchaId。
用户点击未被处理
(单元格未高亮或返回为空) — 请确保点击坐标或索引已发送到服务器,数据被正确序列化,并且在移动设备上支持触控事件。
点击正确但验证码未通过
请检查验证码在验证前未被重新生成,正确答案按会话单独存储,返回格式符合预期,并且验证码的有效期(TTL)未过期。
CAPTCHA 已过期
若用户长时间解决 CAPTCHA,可增加 TTL,重试时刷新 CAPTCHA,并提示用户重新加载图像。
验证防护的可靠性
集成完成后,务必确认系统确实能够抵御自动化行为。
尝试 不点击 提交表单——服务器应拒绝;
提交随机点击或坐标——验证应失败;
提交 已过期 CAPTCHA 的答案——服务器应拒绝;
不要重复使用已解决的 CAPTCHA——再次验证应不可行;
进行负载测试(例如 k6 或 JMeter)——大量请求时:
- 图像应正确生成;
- 服务器不应混淆不同用户的答案;
- 点击验证应保持稳定。
安全与优化建议
仅在 服务器端 存储正确答案(内存、Redis 或数据库),绝不传给客户端。
使用唯一验证码 ID(captchaId)。
限制每个 CAPTCHA 的尝试次数。
传输图像和点击信息时始终使用 HTTPS。
缓存图像并使用 临时 URL(如可能)。
记录生成和验证错误(时间、IP/fingerprint、拒绝原因)。
定期更新 CAPTCHA 机制(图像集、网格尺寸/结构、任务类型)。
支持的验证码
结论
如果你遇到已经安装 CAPTCHA 或其他保护系统的网站,且无法访问代码,不必担心!确定技术类型相对容易。可在隔离测试环境中使用 CapMonster Cloud 检查正确性,确保 token 处理和验证逻辑正常。
对于 图像 CAPTCHA —— 只需识别系统、研究其行为并确保保护功能正常。本文展示了如何识别图像 CAPTCHA (ComplexImage) 以及如何集成或重新配置,以便有效管理和控制保护。
实用链接
关于 ComplexImage 的常见问题
如何将图像 CAPTCHA 发送到 CapMonster Cloud 自动求解?
ComplexImageTask 请求中 metadata.Task 参数控制什么?
CapMonster Cloud 如何返回答案 — 文本、坐标还是网格?
CapMonster Cloud 对图像 CAPTCHA 提交期望何种 Base64 格式?
为什么 Web 开发者绝不能将 CAPTCHA 答案发送到客户端?