验证码安全的核心在于逻辑绑定与后端校验,而非图片生成本身;需用random_int()生成字符、TTF字体加随机扭曲、session强绑定、一次性校验并清空,禁用base64嵌入和固定模式。

PHP 生成验证码图片本身不安全,真正安全的是「验证码逻辑 + 图片生成 + 后端校验」三者配合;单独靠图片防不了自动化攻击。
为什么 imagecreate 生成的验证码容易被绕过
纯 GD 绘制的验证码(比如只加噪点、简单扭曲)在现代 OCR 工具或模型面前基本形同虚设。很多项目用 imagestring 写几个字母+旋转+杂色,结果被 tesseract 一跑就识别率超 70%。
关键不在“画得像不像”,而在“是否可预测、是否绑定会话、是否一次性”:
-
$_SESSION['captcha_code']必须在生成图片前写入,且仅校验一次 - 图片中不能直接 encode 字符串(如 base64 嵌入到
src属性里),否则前端可提取 - 避免固定字体、固定长度、无干扰色块——这些都会降低识别难度
- 不要用
rand(),改用random_int()生成字符
用 imagettftext 加真字体扭曲比 imagestring 更难识别
GD 自带的位图字体太规则,OCR 训练数据里大量覆盖。换系统级 TTF 字体并叠加非线性变形,能显著提升识别成本。
实操要点:
- 确保服务器有可用的中文字体(如
/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf),Windows 下路径类似C:\Windows\Fonts\simhei.ttf - 对每个字符单独调用
imagettftext,随机$angle(±15°)、$x/$y偏移(±3px) - 最后用
imagefilter($im, IMG_FILTER_GAUSSIAN)轻度模糊,别过度——否则人眼也难认 - 禁用
header('Content-Type: image/png')前输出任何空格或 BOM,否则图片显示为损坏
示例片段:
font-size:12px;">$im = imagecreatetruecolor(120, 40); $bg = imagecolorallocate($im, 240, 240, 240); imagefilledrectangle($im, 0, 0, 120, 40, $bg); $code = ''; $chars = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'; for ($i = 0; $i <h3>校验时必须忽略大小写且清空 session</h3><p>用户输入 <code>AbC1</code>,后端拿 <code>strtolower($_POST['captcha']) === $_SESSION['captcha_code']</code> 判断,然后立刻 <code>unset($_SESSION['captcha_code'])</code>。</p><p>常见错误:</p>
- 用
==比较,遇到0e123类似字符串可能触发类型转换漏洞 - 校验失败后没清 session,导致同一码可重复提交
- 前端用 JS 转大写再提交,但后端仍按原样比——大小写不一致直接失败
- 没检查
$_SESSION['captcha_code']是否存在,未设置时直接 fatal error 或跳过校验
真正难的不是画图,是让每次请求的验证码和 session 状态强绑定、不可预测、不可重放。图片只是载体,逻辑才是防线。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











