验证码常用于登录、注册、找回密码、留言提交等场景,用来降低恶意刷接口、批量提交和简单脚本攻击的风险。PHP 可以通过 GD 扩展生成图片验证码,并把验证码答案保存到 Session 中,提交表单时再进行校验。
不过验证码不是万能安全方案。它适合挡住低成本自动化请求,但也可能影响用户体验。本文从图片验证码实现讲起,再介绍 Session 校验、刷新机制和防刷思路。
基本流程
一个常见图片验证码流程包括:后端生成随机字符;把答案保存到 Session;用 PHP 输出验证码图片;前端表单展示图片;用户提交验证码;后端从 Session 中取出答案进行比对。
关键点是:验证码答案不能放在前端页面里,也不能通过接口直接返回给浏览器。前端只能看到图片,真正答案保存在服务端 Session 中。
开启 GD 扩展
PHP 生成图片验证码通常依赖 GD 扩展。使用前要确认环境中已经启用 GD。
if (!extension_loaded('gd')) {
exit('GD 扩展未启用');
}
如果服务器没有 GD,也可以改用第三方验证码服务,或生成更简单的文本问题,但安全性和体验要重新评估。

生成随机字符
验证码字符可以从去掉易混淆字符的集合中随机选择,比如避免 0、O、1、l 混在一起。
function generateCode($length = 4) {
$chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
$code = '';
for ($i = 0; $i < $length; $i++) {
$code .= $chars[random_int(0, strlen($chars) - 1)];
}
return $code;
}
random_int() 比普通随机函数更适合安全相关场景。验证码长度不要太短,4 到 6 位比较常见。
保存到 Session
生成验证码后,把答案保存到 Session 中。校验时再从 Session 里取出。
session_start();
$code = generateCode();
$_SESSION['captcha'] = strtolower($code);
通常可以统一转成小写保存,这样校验时不区分大小写,减少用户输入成本。
输出图片
使用 GD 创建图片、设置背景色、写入文字,并输出 PNG 图片。
$image = imagecreatetruecolor(120, 40);
$bg = imagecolorallocate($image, 245, 245, 245);
$textColor = imagecolorallocate($image, 30, 30, 30);
imagefill($image, 0, 0, $bg);
imagestring($image, 5, 30, 12, $code, $textColor);
header('Content-Type: image/png');
imagepng($image);
imagedestroy($image);
实际项目中可以增加干扰线、噪点、字体变化等,但不要过度复杂。验证码太难识别,会严重影响正常用户。
前端展示
前端页面中用 img 标签加载验证码接口。点击图片时可以追加时间戳刷新,避免浏览器缓存旧图片。
<img id="captcha" src="/captcha.php" alt="验证码">
captcha.onclick = function () {
captcha.src = '/captcha.php?t=' + Date.now();
};
验证码图片要有明确提示,比如“看不清?点击刷新”。不要让用户不知道如何换一张。
提交校验
表单提交时,从用户输入中读取验证码,与 Session 中保存的值比较。
session_start();
$input = strtolower(trim($_POST['captcha'] ?? ''));
$answer = $_SESSION['captcha'] ?? '';
if ($input === '' || $input !== $answer) {
exit('验证码错误');
}
校验完成后,建议立即清除 Session 中的验证码,避免同一个验证码被重复使用。
unset($_SESSION['captcha']);
过期时间
验证码应该有有效期。可以在生成时记录时间戳,提交时判断是否超时。
$_SESSION['captcha_time'] = time();
if (time() - ($_SESSION['captcha_time'] ?? 0) > 300) {
exit('验证码已过期');
}
常见有效期可以设置为 3 到 5 分钟。过期后提示用户刷新验证码。
防刷思路
验证码只能解决一部分问题。真正的防刷还要结合频率限制、IP 限制、账号限制、失败次数限制、行为分析和日志监控。
比如同一 IP 1 分钟内请求验证码过多,可以临时限制;同一账号连续登录失败,可以要求更严格验证;接口层也要做速率限制。
用户体验
验证码会增加用户操作成本。不是所有表单都需要验证码。普通搜索、低风险留言可以先用频率限制;只有登录失败多次、注册高风险、接口被刷时,再引入验证码。
如果验证码总是很难识别,正常用户会流失。安全和体验要平衡。
常见错误
第一种错误是把验证码答案放到前端隐藏字段。第二种错误是校验成功后不清除验证码。第三种错误是没有过期时间。第四种错误是只靠验证码防刷,不做频率限制。第五种错误是验证码图片被缓存,用户看到旧图。
实践建议
PHP 图片验证码可以按这个流程实现:生成随机字符,保存小写答案和时间戳到 Session,输出图片,前端点击刷新,提交时比对并清除答案。验证码接口和提交接口都要配合频率限制。
验证码是防刷体系的一部分,不是全部。把 Session 校验、过期控制、一次性使用和请求频率限制结合起来,才更适合真实业务。













暂无评论内容