PHP生成字母验证码需用str_shuffle()配合剔除易混淆字符(O/0/I/l/1)的字符集,GD绘图时用imagefttext()指定跨平台英文字体、控制干扰线避开字符主干,并通过session安全存储与hash_equals()校验。

PHP生成含字母的验证码字符串
默认的 rand() 或 mt_rand() 生成纯数字太弱,必须混入大小写字母才能提升基础防刷能力。关键不是“能不能”,而是怎么避免生成易混淆字符(比如 O 和 0、I 和 1)以及保证随机性不被预测。
- 用
str_shuffle()+ 预定义字符集最稳妥,别手写循环拼接 - 字符集建议剔除
O、0、I、l、1—— 这些在小字号或低对比度下根本分不清 - 长度至少 4 位,6 位更常见;过短易暴力,过长用户输入意愿下降
- 别用
uniqid()或时间戳做验证码主体,它们不具备密码学随机性,容易被推测
$chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789'; $code = substr(str_shuffle($chars), 0, 6);
GD库绘图时字母扭曲和干扰线控制
光有字符串没用,得画出来。GD 默认不抗锯齿,小字号字母边缘发虚,反而降低识别率。重点不是“加多少干扰线”,而是让干扰不覆盖字符主干。
- 先用
imagecreatetruecolor()创建画布,宽高按字符数动态算(比如每字符 20px 宽 + 5px 间隔) - 用
imagecolorallocate()分配至少 3 种颜色:背景、字符、干扰元素,别全用灰色 - 干扰线用
imageline()画 2–4 条,坐标随机但起点/终点避开字符中心区域 - 字符用
imagefttext()(不是imagestring()),指定字体文件路径,否则 Linux 下常 fallback 到点阵字,字母变形严重
session存储与验证码校验失败的典型原因
用户输完提交,后端比对总是失败?90% 不是生成逻辑错,而是 session 没对上或被覆盖。
- 生成验证码前必须调用
session_start(),且不能有任何输出(包括 BOM、空格、echo) - 存到
$_SESSION['captcha_code']后,立即用strtolower()统一转小写再存 —— 前端输入习惯不区分大小写,后端比对也该忽略大小写 - 校验时用
hash_equals()而非==,防止时序攻击(虽然验证码场景风险低,但养成习惯) - 校验成功后立刻
unset($_SESSION['captcha_code']),避免重复使用
中文环境下字体缺失导致字母显示为方块
Linux 服务器没装中文字体,但用了中文字体路径去画英文验证码,结果所有字母变成 □。这不是 PHP 错,是 GD 找不到字形数据。
立即学习“PHP免费学习笔记(深入)”;
- 确认字体文件真实存在:
file_exists('/path/to/arial.ttf'),别信文档里写的默认路径 - 优先用无版权的英文字体,如
DejaVuSans.ttf(Ubuntu 自带)、FreeSans.ttf(CentOS 可yum install ghostscript-fonts) - 测试时直接输出字体路径和
is_readable()结果,别靠猜 - Windows 开发环境用
arial.ttf没问题,但上线前必须切到跨平台字体
真正麻烦的不是生成字母,是让生成的字母在各种设备、浏览器、缩放比例下都清晰可辨,同时不让机器轻易 OCR 识别 —— 这需要微调倾斜角度、字符间距、噪点密度,而不是堆参数。



















