在Web登录或注册页面中,使用PHP结合GD库生成验证码是最基础也最实用的防护手段之一。通过随机字符配合干扰线,可以有效阻挡大部分简单的自动提交脚本。下面直接看一段可运行的完整示例,再逐步拆解其中的技术细节。

一、基础环境准备
GD库是PHP处理图像的扩展,多数Linux发行版通过php-gd包提供。在写代码前,先用php -m | grep gd确认已加载。若未启用,需修改php.ini取消extension=gd前的注释并重启服务。
验证码通常依赖Session保存正确值,因此调用session_start()必须位于任何输出之前。很多新手把图片输出和HTML混排,导致header报错,这点后面会专门说明。
二、完整生成代码示例
以下代码演示了创建画布、写随机字符、画干扰线并输出PNG的整个过程。注意所有小于号、大于号在pre内部都已转义。
<?php
session_start();
// 1. 创建画布
$width = 120;
$height = 40;
$image = imagecreatetruecolor($width, $height);
// 2. 分配颜色
$bgColor = imagecolorallocate($image, 240, 240, 240);
imagefill($image, 0, 0, $bgColor);
// 3. 生成随机字符
$chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
$length = 4;
$code = '';
for ($i = 0; $i < $length; $i++) {
$char = $chars[rand(0, strlen($chars) - 1)];
$code .= $char;
$textColor = imagecolorallocate($image, rand(0, 120), rand(0, 120), rand(0, 120));
$x = 15 + $i * 25;
$y = rand(25, 35);
imagestring($image, 5, $x, $y, $char, $textColor);
}
$_SESSION['captcha'] = $code;
// 4. 绘制干扰线
for ($i = 0; $i < 5; $i++) {
$lineColor = imagecolorallocate($image, rand(100, 220), rand(100, 220), rand(100, 220));
imageline($image, rand(0, $width), rand(0, $height), rand(0, $width), rand(0, $height), $lineColor);
}
// 5. 输出图像
header('Content-Type: image/png');
imagepng($image);
imagedestroy($image);
?>
上述脚本直接访问时会返回一张图片。字符集去掉了易混的0、O、1、I,降低用户辨认难度。每个字符的纵坐标随机偏移,让机器切分字符更困难。
干扰线使用imageline()随机起止点,颜色与背景有一定反差但不刺眼。若线条过密会影响真人阅读,一般控制在3到6条之间。
三、使用TTF字体提升安全性
imagestring()仅用内置字体,容易被针对性识别。更优方案是imagettftext()加载TrueType字体,支持旋转与大小调整。
<?php
$font = '/var/www/fonts/arial.ttf';
for ($i = 0; $i < $length; $i++) {
$angle = rand(-15, 15);
$size = rand(18, 24);
imagettftext($image, $size, $angle, 15 + $i * 28, 30, $textColor, $font, $code[$i]);
}
?>
字体文件需服务器可读,路径写绝对地址最稳妥。角度随机让字符轻微倾斜,OCR通用模型准确率明显下降,而人眼仍可在瞬间读完。
要注意imagettftext()的编码必须为UTF-8,若字符乱码多是文件或字符串编码不匹配,可用mb_convert_encoding()转换。
四、常见坑与排查
第一,Session跨页丢失:表单页和验证页若不在同一域名或路径,session_id可能不一致。建议统一入口或显式传递。
第二,图像破损:在header()前有任何echo或空白,PNG头会被污染。用ob_clean()清空缓冲再输出可应急,但根本办法是分离验证码脚本。
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| 页面显示乱码方块 | 字体路径错误 | 检查文件权限与绝对路径 |
| 验证码总不匹配 | Session未开启 | 在脚本首行加session_start() |
| 图片裂图 | 输出了多余字符 | 删除所有空白与echo |
把验证值存入Session后,表单提交时比对$_POST['input']与$_SESSION['captcha']即可。比对前统一转大写,避免用户大小写纠结。
五、小结与扩展
GD库方案零依赖、性能好,适合低中等安全场景。若需更强抗机器能力,可加干扰点、正弦曲线或融合前端点击行为验证。
生产环境建议给验证码接口限流,防止攻击者高频刷新消耗CPU。同时定期换字体与字符集,让已训练的识别脚本失效。