你是否在寻找用PHP实现验证码生成与验证的案例

wen PHP项目 48

手把手教你用PHP实现验证码生成与验证:从入门到实战(附完整代码)

目录导读

  1. 为什么还需要学习PHP验证码?——开发者痛点与需求分析
  2. 验证码的核心原理:从图像生成到Session校验的全链路拆解
  3. 手写一个最简单的PHP验证码(GD库版)
  4. 进阶:带干扰元素的安全验证码(噪点+线条+旋转字体)
  5. 前端+后端联调:AJAX实时校验验证码
  6. 常见踩坑与解决方法(Session失效、跨域、缓存问题)
  7. 问答环节:开发者最常问的6个验证码问题
  8. 安全警示:这些验证码写法会被秒破

为什么还需要学习PHP验证码?

痛点场景:你是否遇到过——表单被机器人疯狂提交、用户注册时手机号被恶意遍历、论坛被灌水机刷帖?
核心需求:验证码是阻挡自动化攻击的第一道防线,虽然Google reCAPTCHA已普及,但许多中小型项目出于成本、隐私合规(如国内无法用reCAPTCHA)或离线环境需求,仍需自建PHP验证码系统。

你是否在寻找用PHP实现验证码生成与验证的案例

数据支撑:根据PortSwigger的2024年安全报告,超过35%的Web应用仍存在表单自动化攻击漏洞,而正确的验证码实现可阻挡99%的机器流量。


验证码的核心原理

1 流程分解

用户请求验证码 -> 服务器生成随机字符串 -> 存储到Session -> 绘制成图片返回 -> 用户输入 -> 提交时比对Session值 -> 成功/失败

2 关键组件

  • 随机码生成:避免使用rand(),改用random_int()openssl_random_pseudo_bytes()
  • 图像绘制:PHP GD库或Imagick(推荐GD,兼容性好)
  • Session存储:注意使用session_start()的顺序和路径
  • 前端交互:通常用<img src="captcha.php">加载,点击刷新时需加上时间戳防缓存

手写一个最简单的PHP验证码(GD库版)

创建验证码生成文件 captcha.php

<?php
session_start();
header('Content-Type: image/png');
// 1. 生成4位随机数
$code = '';
$chars = '23456789abcdefghjkmnpqrstuvwxyz'; // 去除容易混淆的0/O/i/l等
for ($i = 0; $i < 4; $i++) {
    $code .= $chars[random_int(0, strlen($chars) - 1)];
}
$_SESSION['captcha'] = strtolower($code); // 统一小写存储
// 2. 创建图片背景
$width = 120;
$height = 40;
$image = imagecreatetruecolor($width, $height);
$bg = imagecolorallocate($image, 255, 255, 255); // 白色背景
imagefill($image, 0, 0, $bg);
// 3. 画文字
$textColor = imagecolorallocate($image, 0, 0, 0); // 黑色
$font = 5; // 内置字体
$x = 20;
$y = 20;
imagestring($image, $font, $x, $y, $code, $textColor);
// 4. 输出并销毁
imagepng($image);
imagedestroy($image);

前端调用

<img src="captcha.php?t=<?php echo time(); ?>" id="captchaImg" onclick="this.src='captcha.php?t='+new Date().getTime()">

后体验证逻辑 check.php

session_start();
$userInput = strtolower($_POST['captcha'] ?? '');
if ($userInput === $_SESSION['captcha']) {
    echo '验证成功';
    unset($_SESSION['captcha']); // 使用后销毁,防止复用
} else {
    echo '验证码错误';
}

注意点imagestring使用PHP内置字体,无法抗OCR,仅适合低安全场景。


进阶:带干扰元素的安全验证码

1 添加噪点与线条

// 在文字绘制后添加干扰:
// 100个随机噪点
for ($i = 0; $i < 100; $i++) {
    $color = imagecolorallocate($image, random_int(0, 150), random_int(0, 150), random_int(0, 150));
    imagesetpixel($image, random_int(0, $width), random_int(0, $height), $color);
}
// 4条随机弧线
for ($i = 0; $i < 4; $i++) {
    $color = imagecolorallocate($image, random_int(0, 200), random_int(0, 200), random_int(0, 200));
    imagearc($image, random_int(0, $width), random_int(0, $height), 
             random_int(10, 50), random_int(10, 50), 0, 360, $color);
}

2 使用TrueType字体抗OCR

$fontFile = __DIR__ . '/arial.ttf'; // 需下载字体文件
$size = 20;
$angle = random_int(-15, 15); // 随机旋转
imagettftext($image, $size, $angle, 20, 30, $textColor, $fontFile, $code);

效果提升:结合随机颜色、背景渐变、字符间距变化,可对抗90%的普通OCR识别工具。


前端+后端联调:AJAX实时校验

1 无刷新验证逻辑

$('#loginForm').on('submit', function(e) {
    e.preventDefault();
    $.ajax({
        url: 'check_captcha.php',
        method: 'POST',
        data: { captcha: $('#captchaInput').val() },
        success: function(response) {
            if (response === 'success') {
                // 继续提交表单
                $('#loginForm')[0].submit();
            } else {
                alert('验证码错误,请重新输入');
                $('#captchaImg').click(); // 刷新验证码
            }
        }
    });
});

关键优化:每次验证失败后强制刷新Session中的验证码值,防止同一个验证码被多次尝试。


常见踩坑与解决方法

1 Session失效问题

  • 现象:每次请求都是新的Session ID
  • 解决:在captcha.php顶部确保没有session_cache_limiter('private, must-revalidate'),并设置session_start()在输出任何内容前。

2 图片缓存问题

  • 现象:始终显示同一张验证码
  • 解决:前端加随机时间戳 ?t=<?php echo microtime(); ?>;后端在header中加:
    header('Cache-Control: no-cache, no-store, must-revalidate');

3 跨域Session共享

如果前端和后端不同域名(如API分离),需使用Token替代Session,将验证码值加密后返回给前端,提交时解密比对。


问答环节:开发者最常问的6个验证码问题

Q1:验证码字母总是不显示,只看到空白?
A:检查GD库是否安装:php -m | grep gd;如果使用imagettftext,确认字体文件路径正确,且PHP有读取权限。

Q2:为什么验证码总是提示错误,明明输入正确?
A:检查大小写统一问题,建议存储和比对时都使用strtolower(),另外检查Session文件是否存在(Linux系统下/tmp目录的写权限)。

Q3:可以用Cookie替代Session吗?
A:不建议,Cookie存储在客户端,容易被篡改,Session存储在服务端更安全。

Q4:验证码能被机器学习破解吗?
A:简单的数字+噪点可以被CNN(卷积神经网络)破解,但通过引入扭曲字体、干扰线条、随机字符颜色可大幅提高破解成本,商业级建议用reCAPTCHA。

Q5:如何实现点击刷新验证码?
A:给图片绑定click事件,重新设置src,并加上时间戳防缓存,参考上文第3步的代码。

Q6:验证码图片如何居中显示?
A:在CSS中设置display: block; margin: 0 auto;,或将图片包裹在<div style="text-align:center;">中。


安全警示:这些验证码写法会被秒破

避坑清单:

  • ❌ 避免使用rand()生成随机码——可预测性高
  • ❌ 避免使用纯数字——OCR识别率极高
  • ❌ 避免不添加干扰——简单图像可直接用Tesseract OCR
  • ❌ 避免在验证码图片路径中泄露code——如captcha.php?code=abc
  • ❌ 避免验证码可用多次——每次验证后必须销毁Session

推荐架构:

验证码 -> 加密存储到Session + 记录时间戳(5分钟后过期) -> 每次验证后立即清除

自建PHP验证码是一门基础但重要的后端技能,从最开始的imagestring简单四位数,到经过抗干扰优化、TTF字体旋转、AJAX联调的完整系统,你已掌握大部分生产环境所需的核心知识,如果需要更安全、更极致的方案(如滑块验证码、算术验证码),可在此基础上扩展——原理不变,只是增强交互与随机性。

如果这篇文章对你有所帮助,欢迎分享给同样被“垃圾注册”困扰的开发者朋友,实战中如果遇到任何问题,欢迎在评论区留言探讨。

抱歉,评论功能暂时关闭!