直接用rand()生成算术题易出错,因其无法保证结果为非负整数、不出现除零或不可心算的组合;且eval()在线上常被禁用。应改用mt_rand()配合运算符白名单与显式计算逻辑,分离题目生成与答案计算,并存入session验证。

为什么直接用 rand() 生成算术题容易出错
因为算术验证码的核心不是“随机”,而是“可控的随机”:需要保证结果是整数、运算符合理、不出现负数或除零,且人类能心算。直接用 rand(1, 9) 拼接 "+" 和 "-",很可能生成 "3 - 7"(负数)或 "5 / 0"(崩溃)。更隐蔽的问题是,PHP 的 eval() 执行字符串表达式虽快,但默认开启 disable_functions 时会报 "Warning: eval(): Disabled for security reasons",线上环境几乎必现。
实操建议:
- 避开
eval(),改用白名单运算符 + 显式计算逻辑 - 加减法限定操作数范围(如 1–12),乘法限制第二操作数 ≤6,除法只生成“被除数 % 除数 === 0”的组合
- 用
mt_rand()替代rand(),随机性更强且无种子重复风险
怎么安全生成带答案的算术题字符串
关键在于分离“题目生成”和“答案计算”,避免运行时解析。比如生成 "8 + 4" 后,立刻用 PHP 运算符算出 12 并存入 session,而不是等用户提交后再 eval("8 + 4")。
示例逻辑(精简版):
// 随机选运算符
$ops = ['+', '-', '×', '÷'];
$op = $ops[array_rand($ops)];
<p>// 根据运算符定制参数
switch ($op) {
case '+':
$a = mt_rand(1, 10);
$b = mt_rand(1, 10);
$answer = $a + $b;
$question = "$a + $b";
break;
case '-':
$a = mt_rand(5, 15); // 确保 a ≥ b
$b = mt_rand(1, $a - 1);
$answer = $a - $b;
$question = "$a - $b";
break;
case '×':
$a = mt_rand(2, 8);
$b = mt_rand(2, 6);
$answer = $a <em> $b;
$question = "$a × $b";
break;
case '÷':
$b = mt_rand(2, 6);
$a = $b </em> mt_rand(2, 8); // 保证整除
$answer = $a / $b;
$question = "$a ÷ $b";
break;
}</p><p>// 存入 session 供验证用(注意开启 session_start())
$_SESSION['captcha_answer'] = (int)$answer;
</p>
如何防止验证码图片被缓存或识别失败
浏览器缓存会导致同一张图反复显示,OCR 工具则可能识别出数字和符号。PHP GD 生成图片时,必须切断缓存链并干扰字符可读性。
实操要点:
- 输出前加三行头:
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');、header('Pragma: no-cache');、header('Expires: Mon, 01 Jan 1990 00:00:00 GMT'); - 不用纯黑字体,用
imagecolorallocate($im, mt_rand(50,150), mt_rand(50,150), mt_rand(50,150))生成灰阶文字色 - 在数字/符号间画 2–3 条斜线干扰(
imageline()),但避开关键像素区域 - 字体大小别超过 20px,否则移动端显示溢出;用
imagettfbbox()测宽高再居中,别硬写坐标
用户提交后怎么验证又快又准
验证环节最容易忽略的是类型转换和空格处理。用户可能输 " 12 " 或 "12.0",而 session 里存的是 (int)12。
推荐验证方式:
- 用
trim()去首尾空格,再用filter_var($input, FILTER_VALIDATE_INT)判断是否为纯整数 - 若允许小数(如除法结果),改用
floatval()+round()对齐精度(除法答案全是整数,无需小数) - 验证通过后立刻
unset($_SESSION['captcha_answer']),防止重放 - 失败时不要返回“答案是 X”,只提示“验证码错误”,避免信息泄露
最常被跳过的细节:GD 库在某些 Docker 镜像或轻量主机上默认不启用,调用 imagecreate() 前先检查 extension_loaded('gd'),否则页面空白且无报错。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











