必须用 imagecreatetruecolor,因其支持真彩色避免锯齿和颜色混杂;验证码明文存 SESSION 但需限时单次使用;校验须服务端强制执行并防绕过;GD 未启用时页面空白且响应头错误;中文验证码易致用户失败,应慎用。

验证码图片生成要用 imagecreate 还是 imagecreatetruecolor
必须用 imagecreatetruecolor。旧版 imagecreate 只支持 256 色调色板,文字边缘容易出现明显锯齿,且在添加干扰线、噪点时颜色混杂严重,导致 OCR 识别率反升——这不是防住了机器人,是帮它降低了难度。
实际生成中还要立刻调用 imageantialias 开启抗锯齿,并用 imagefilledrectangle 填充纯白底色,避免默认黑色背景干扰人眼识别。
$_SESSION['captcha'] 存的是明文还是哈希值
存明文字符串(如 'K7mR'),但必须搭配有效期和单次使用限制。校验时不能只比对相等,要同步执行:
• 检查 $_SESSION['captcha_time'] 是否未超时(建议 120 秒)
• 校验通过后立即 unset($_SESSION['captcha']) 和 unset($_SESSION['captcha_time'])
• 若用户连续 3 次输错,可额外加锁 60 秒(写入 $_SESSION['captcha_lock_until'] 时间戳)
不加密存储不是偷懒——验证码本就是短期、单次、低敏感的凭证,加盐哈希反而增加校验延迟,还可能因 session 序列化问题导致比对失败。
前端提交验证码时怎么防止绕过校验
关键在服务端不做任何“前端已填就放行”的假设:
• 所有表单提交接口必须先调用校验函数,无论请求头是否含 X-Requested-With
• 不依赖 isset($_POST['captcha']) 判断是否提交,而应统一提取 $_POST['captcha'] ?? '' 后直接 trim + 转大写再比对
• 禁用客户端自动填充:在 input 上加 autocomplete="off" 和 autocapitalize="characters"(iOS 键盘强制大写)
• 验证码输入框不要设 type="text" 以外的类型,type="password" 会隐藏字符但不增强安全,反而影响无障碍访问
function verify_captcha($input): bool {
if (empty($_SESSION['captcha']) || !isset($_SESSION['captcha_time'])) {
return false;
}
if (time() - $_SESSION['captcha_time'] > 120) {
unset($_SESSION['captcha'], $_SESSION['captcha_time']);
return false;
}
$expected = strtoupper(trim((string)$input));
$result = hash_equals($_SESSION['captcha'], $expected);
if ($result) {
unset($_SESSION['captcha'], $_SESSION['captcha_time']);
}
return $result;
}GD 扩展没启用或字体文件路径出错时的错误表现
典型现象不是报“GD not found”,而是:
• 图片生成页返回空白,但 HTTP 状态码是 200
• 查看响应头发现 Content-Type: text/html(应为 image/png)
• 错误日志里有 PHP Warning: imagettftext(): Could not find/open font 或 PHP Warning: imagepng(): Cannot open output stream
解决步骤:
• 用 extension_loaded('gd') 和 gd_info() 在脚本开头快速确认 GD 状态
• 字体路径必须是绝对路径,__DIR__ . '/fonts/arial.ttf' 比 './fonts/arial.ttf' 可靠得多
• Windows 下注意反斜杠转义,推荐统一用 str_replace('\', '/', $path) 处理
立即学习“PHP免费学习笔记(深入)”;
实际最难处理的是中文验证码——不是技术做不到,而是绝大多数场景下,中文字符集增大、易混淆字(如“零”和“O”、“一”和“l”)导致用户反复输入失败。真要上中文,得筛掉形近字、禁用多音字、并强制语音播报辅助,这已超出基础验证码范畴。











