本文目录导读:

我来详细介绍PHP无感验证码(无感验证)的实现方法,无感验证码是一种不需要用户手动操作的验证机制,通过分析用户行为特征来区分人类用户和机器人。
无感验证码的常见实现方式
基于行为分析的滑动验证
<?php
// 简单的无感滑动物理验证
class SliderCaptcha {
private $sessionKey = 'captcha_slider_data';
// 生成滑块验证图
public function generate() {
$width = 300;
$height = 150;
$sliderWidth = 40;
$sliderHeight = 40;
// 生成随机滑块位置(不直接返回给前端)
$targetX = rand(50, $width - $sliderWidth - 20);
$targetY = rand(20, $height - $sliderHeight - 20);
// 创建图片
$img = imagecreatetruecolor($width, $height);
$bg = imagecolorallocate($img, 255, 255, 255);
imagefill($img, 0, 0, $bg);
// 绘制背景干扰元素
$this->addNoise($img);
// 存储位置信息(加密存储)
$_SESSION[$this->sessionKey] = [
'x' => $targetX,
'y' => $targetY,
'timestamp' => time()
];
// 返回图片和滑块位置给前端
return [
'image' => base64_encode($this->getImageData($img)),
'slider_x' => $targetX,
'slider_y' => $targetY
];
}
// 验证用户滑动位置
public function verify($userX, $userY) {
if (!isset($_SESSION[$this->sessionKey])) {
return false;
}
$data = $_SESSION[$this->sessionKey];
// 检查时间是否过期(10秒内)
if (time() - $data['timestamp'] > 10) {
unset($_SESSION[$this->sessionKey]);
return false;
}
// 允许±5像素误差
$tolerance = 5;
$isMatch = abs($userX - $data['x']) <= $tolerance &&
abs($userY - $data['y']) <= $tolerance;
if ($isMatch) {
unset($_SESSION[$this->sessionKey]);
}
return $isMatch;
}
// 添加干扰元素
private function addNoise($img) {
for ($i = 0; $i < 50; $i++) {
$color = imagecolorallocate($img, rand(0, 255), rand(0, 255), rand(0, 255));
imageline($img, rand(0, 300), rand(0, 150), rand(0, 300), rand(0, 150), $color);
}
}
private function getImageData($img) {
ob_start();
imagepng($img);
$data = ob_get_contents();
ob_end_clean();
imagedestroy($img);
return $data;
}
}
// 使用示例
$captcha = new SliderCaptcha();
// 生成验证码
$captchaData = $captcha->generate();
// 前端获取图片
echo json_encode([
'code' => 200,
'data' => [
'image' => 'data:image/png;base64,' . base64_encode($captchaData['image']),
'position' => ['x' => $captchaData['slider_x'], 'y' => $captchaData['slider_y']]
]
]);
?>
基于时间行为的验证
<?php
class TimeBasedCaptcha {
private $sessionKey = 'captcha_time_data';
private $minTime = 3; // 最小操作时间(秒)
private $maxTime = 10; // 最大操作时间(秒)
// 开始记录时间
public function start() {
$_SESSION[$this->sessionKey] = [
'start_time' => microtime(true),
'uid' => $this->generateUid()
];
}
// 验证时间是否合理
public function verify() {
if (!isset($_SESSION[$this->sessionKey])) {
return false;
}
$data = $_SESSION[$this->sessionKey];
$elapsed = microtime(true) - $data['start_time'];
// 正常人类操作需要时间,机器人速度太快
if ($elapsed < $this->minTime || $elapsed > $this->maxTime) {
unset($_SESSION[$this->sessionKey]);
return false;
}
// 验证UID
if ($data['uid'] !== $this->generateUid()) {
return false;
}
unset($_SESSION[$this->sessionKey]);
return true;
}
private function generateUid() {
return md5(uniqid() . session_id());
}
}
?>
基于Token的无感验证
<?php
class TokenCaptcha {
private $tokenKey = 'captcha_token';
private $expireTime = 300; // 5分钟过期
// 生成token
public function generateToken() {
$token = bin2hex(random_bytes(32));
$payload = [
'token' => $token,
'expire' => time() + $this->expireTime,
'data' => $this->getUserBehavior()
];
// 存储到Redis或数据库
$_SESSION[$this->tokenKey] = base64_encode(json_encode($payload));
return $token;
}
// 验证token
public function verifyToken($token) {
if (!isset($_SESSION[$this->tokenKey])) {
return false;
}
$payload = json_decode(base64_decode($_SESSION[$this->tokenKey]), true);
// 检查token是否匹配
if ($payload['token'] !== $token) {
return false;
}
// 检查过期时间
if (time() > $payload['expire']) {
unset($_SESSION[$this->tokenKey]);
return false;
}
// 检查行为特征
if (!$this->validateBehavior($payload['data'])) {
return false;
}
unset($_SESSION[$this->tokenKey]);
return true;
}
// 获取用户行为特征
private function getUserBehavior() {
return [
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
'cookie_enabled' => isset($_COOKIE['PHPSESSID']),
'js_enabled' => true
];
}
// 验证行为特征
private function validateBehavior($behavior) {
// 检查User-Agent
if (empty($behavior['user_agent']) ||
preg_match('/bot|spider|crawler/i', $behavior['user_agent'])) {
return false;
}
// 检查IP是否可信
if (empty($behavior['ip'])) {
return false;
}
return true;
}
}
?>
完整的无感验证码类
<?php
class InvisibleCaptcha {
private $sessionKey = 'invisible_captcha';
private $redis;
public function __construct($redis = null) {
if ($redis) {
$this->redis = $redis;
}
}
// 前端埋点收集数据
public function collectData($data) {
$fingerprint = $this->generateFingerprint($data);
$this->saveData($fingerprint, [
'mouse_move' => $data['mouse_move'] ?? [],
'key_press' => $data['key_press'] ?? [],
'timing' => $data['timing'] ?? []
]);
http_response_code(200);
return ['status' => 'success'];
}
// 生成指纹
private function generateFingerprint($data) {
return md5(json_encode([
'ua' => $_SERVER['HTTP_USER_AGENT'],
'ip' => $_SERVER['REMOTE_ADDR'],
'time' => time()
]));
}
// 验证数据
public function verify() {
$fingerprint = $this->getCurrentFingerprint();
if ($this->checkHumanBehavior($fingerprint)) {
$this->setVerifiedFlag();
return true;
}
return false;
}
// 检查人类行为
private function checkHumanBehavior($fingerprint) {
$data = $this->getData($fingerprint);
if (!$data) {
return false;
}
// 检查鼠标移动轨迹
$mouseMove = $data['mouse_move'];
if (count($mouseMove) < 10) {
return false;
}
// 检查操作时间间隔
$intervals = [];
foreach ($mouseMove as $i => $point) {
if ($i > 0) {
$intervals[] = $point['time'] - $mouseMove[$i-1]['time'];
}
}
// 人类操作有规律的时间间隔
$avgInterval = array_sum($intervals) / count($intervals);
if ($avgInterval < 5 || $avgInterval > 2000) {
return false;
}
// 检查键盘输入
if (isset($data['key_press']) && count($data['key_press']) > 20) {
return false;
}
return true;
}
// 设置验证通过标识
private function setVerifiedFlag() {
$_SESSION['captcha_verified'] = true;
$_SESSION['captcha_verified_time'] = time();
}
// 检查是否已验证
public function isVerified() {
return isset($_SESSION['captcha_verified']) &&
$_SESSION['captcha_verified'] === true &&
(time() - $_SESSION['captcha_verified_time']) < 600; // 10分钟内有效
}
}
?>
前端JavaScript配合
// 前端收集行为数据
class InvisibleCaptchaCollector {
constructor() {
this.data = {
mouse_move: [],
key_press: [],
timing: []
};
this.startTime = Date.now();
this.init();
}
init() {
// 监听鼠标移动
document.addEventListener('mousemove', this.onMouseMove.bind(this));
// 监听键盘输入
document.addEventListener('keydown', this.onKeyDown.bind(this));
// 页面加载时间
this.data.timing.push({
load_time: performance.now()
});
}
onMouseMove(e) {
// 每100ms记录一个点
if (this.lastMouseTime && Date.now() - this.lastMouseTime < 100) {
return;
}
this.lastMouseTime = Date.now();
this.data.mouse_move.push({
x: e.clientX,
y: e.clientY,
time: Date.now() - this.startTime
});
// 限制数据大小
if (this.data.mouse_move.length > 100) {
this.data.mouse_move.shift();
}
}
onKeyDown(e) {
// 记录键盘输入
this.data.key_press.push({
key: e.key,
time: Date.now() - this.startTime,
duration: 0
});
// 限制数据大小
if (this.data.key_press.length > 50) {
this.data.key_press.shift();
}
}
// 提交数据
submit() {
fetch('/api/captcha/collect', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(this.data)
}).then(response => response.json());
}
}
// 使用示例
const collector = new InvisibleCaptchaCollector();
// 表单提交时
document.getElementById('myForm').onsubmit = function(e) {
e.preventDefault();
// 收集数据
collector.submit();
// 提交表单数据
fetch('/api/submit', {
method: 'POST',
body: new FormData(this)
});
};
第三方服务集成
使用极验无感验证
<?php
require_once 'vendor/autoload.php';
use GeeTeam\GeetestLib;
class GeetestCaptcha {
private $captcha;
public function __construct() {
$this->captcha = new GeetestLib('你的Geetest_ID', '你的Geetest_KEY');
}
// 初始化验证
public function initGeeTest() {
$status = $this->captcha->preProcess();
$_SESSION['gtserver'] = $status ? 1 : 0;
return $this->captcha->getResponse();
}
// 验证结果
public function verifyGeetest($challenge, $validate, $seccode) {
$result = $this->captcha->successValidate($challenge, $validate, $seccode);
if ($result) {
return true;
}
return false;
}
}
?>
最佳实践建议
- 多因素结合:结合行为分析、时间检测、Token验证等多种方式
- 风险分级:根据风险评估结果,选择不同的验证强度
- 监控与日志:记录验证失败记录,分析潜在攻击
- 性能优化:使用Redis缓存验证数据,避免频繁访问数据库
- 前端配合:在前端合理收集用户行为数据,不干扰正常用户体验
无感验证码的目标是提供良好的用户体验,同时有效区分正常用户和机器人,根据业务需求选择合适的实现方式,并在安全性、用户体验和成本之间找到平衡点。