PHP生成验证码教程:图片验证码、Session校验与防刷思路

验证码常用于登录、注册、找回密码、留言提交等场景,用来降低恶意刷接口、批量提交和简单脚本攻击的风险。PHP 可以通过 GD 扩展生成图片验证码,并把验证码答案保存到 Session 中,提交表单时再进行校验。

不过验证码不是万能安全方案。它适合挡住低成本自动化请求,但也可能影响用户体验。本文从图片验证码实现讲起,再介绍 Session 校验、刷新机制和防刷思路。

基本流程

一个常见图片验证码流程包括:后端生成随机字符;把答案保存到 Session;用 PHP 输出验证码图片;前端表单展示图片;用户提交验证码;后端从 Session 中取出答案进行比对。

关键点是:验证码答案不能放在前端页面里,也不能通过接口直接返回给浏览器。前端只能看到图片,真正答案保存在服务端 Session 中。

开启 GD 扩展

PHP 生成图片验证码通常依赖 GD 扩展。使用前要确认环境中已经启用 GD。

if (!extension_loaded('gd')) {
  exit('GD 扩展未启用');
}

如果服务器没有 GD,也可以改用第三方验证码服务,或生成更简单的文本问题,但安全性和体验要重新评估。

PHP验证码教程配图:图片验证码与Session校验
验证码的重点不是把图片做复杂,而是让答案保存在服务端,并控制提交频率。

生成随机字符

验证码字符可以从去掉易混淆字符的集合中随机选择,比如避免 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 校验、过期控制、一次性使用和请求频率限制结合起来,才更适合真实业务。

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容