首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 实现算术运算验证码:动态生成算式图片与会话校验完整方案

     編輯:PHP 相關知識

摆脱纯字符识别,用简单数学题提升用户体验与安全性的轻量级验证码方案

传统字符验证码对用户不友好,且易被 OCR 破解。算术运算验证码要求用户计算简单加减乘除,既提高了机器识别成本,又降低了用户认知负担。本文提供基于 GD 库的完整实现,含前端表单、后端生成/校验、安全加固建议。

核心架构设计

+----------+     1. 请求验证码图片      +------------------+
|  浏览器  | -------------------------> |  img.php (生成端) |
|          | <------------------------- |  - 随机生成算式  |
|          |    2. 返回 PNG 图片流       |  - GD 绘制干扰图  |
|          |                            |  - 答案存入 Session |
+----------+                            +------------------+
       |                                        ^
       | 3. 用户输入答案提交表单                 | 4. 校验答案
       v                                        |
+----------+                            +------------------+
|  demo.php| -------------------------> |  demo.php (校验端)|
| (表单页) |    POST 答案 + Session ID  |  - 取 Session 答案 |
|          |                            |  - MD5 大小写不敏感比对 |
|          | <------------------------- |  - 返回通过/失败     |
+----------+                            +------------------+

前端表单页 (demo.php)

<?php
session_start();

// 处理登录提交
if (!empty($_POST['login'])) {
    $input = strtoupper(trim($_POST['captcha']));
    $expected = $_SESSION['captcha_answer'] ?? '';
    if (md5($input) === $expected) {
        echo '<div style="color:green">验证通过,欢迎登录!</div>';
    } else {
        echo '<div style="color:red">验证码错误,请重试。</div>';
    }
}
?>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>算术验证码演示</title>
    <style>
        .captcha-img { vertical-align: middle; cursor: pointer; border: 1px solid #ddd; }
        .captcha-img:hover { opacity: 0.8; }
        input[name="captcha"] { width: 80px; text-align: center; }
    </style>
</head>
<body>
    <h3>登录验证</h3>
    <form method="post">
        <label>用户名: <input type="text" name="username" required></label><br><br>
        <label>密码: <input type="password" name="password" required></label><br><br>
        <label>验证码: 
            <input type="text" name="captcha" maxlength="10" required autocomplete="off">
            <img class="captcha-img" src="img.php?" id="captchaImg" alt="点击刷新" title="点击刷新">
        </label><br><br>
        <button type="submit" name="login">登录</button>
    </form>
    <script>
        document.getElementById('captchaImg').onclick = function() {
            this.src = 'img.php?' + Date.now(); // 防缓存刷新
        };
    </script>
</body>
</html>

验证码生成核心 (img.php)

<?php
session_start();

header('Content-Type: image/png');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');

// 配置参数
$width  = isset($_GET['w']) ? max(80, min(300, (int)$_GET['w'])) : 120;
$height = isset($_GET['h']) ? max(30, min(100, (int)$_GET['h'])) : 40;
$fontSize = max(14, min(28, (int)($height * 0.6)));

// 1. 生成随机算式 (仅加减法,避免负数/除不尽)
function generateExpression(): array {
    $a = random_int(1, 20);
    $b = random_int(1, 20);
    $op = random_int(0, 1) ? '+' : '-';
    if ($op === '-' && $a < $b) [$a, $b] = [$b, $a]; // 保证非负
    $expr = "$a $op $b";
    $answer = $op === '+' ? $a + $b : $a - $b;
    return ['expr' => $expr, 'answer' => (string)$answer];
}

[$expr, $answer] = generateExpression();

// 2. 存入 Session (MD5 大写,校验时大小写不敏感)
$_SESSION['captcha_answer'] = md5(strtoupper($answer));

// 3. GD 绘制
$img = imagecreatetruecolor($width, $height);

// 颜色分配
$bgColor = imagecolorallocate($img, 245, 248, 252);      // 浅蓝灰背景
$textColor = imagecolorallocate($img, 30, 60, 120);      // 深蓝文字
$noiseColor = imagecolorallocate($img, 180, 200, 220);   // 干扰线颜色
$pixelColor = imagecolorallocate($img, 150, 170, 190);   // 噪点颜色

// 填充背景
imagefill($img, 0, 0, $bgColor);

// 干扰元素:噪点
for ($i = 0; $i < $width * $height / 15; $i++) {
    imagesetpixel($img, random_int(0, $width-1), random_int(0, $height-1), $pixelColor);
}

// 干扰元素:波浪线
for ($i = 0; $i < 4; $i++) {
    $y = random_int($height/4, 3*$height/4);
    $amp = random_int(3, 8);
    for ($x = 0; $x < $width; $x++) {
        $py = $y + (int)($amp * sin($x / 10));
        if ($py >= 0 && $py < $height) imagesetpixel($img, $x, $py, $noiseColor);
    }
}

// 干扰元素:随机直线
for ($i = 0; $i < 3; $i++) {
    imageline($img, random_int(0, $width), random_int(0, $height), random_int(0, $width), random_int(0, $height), $noiseColor);
}

// 绘制算式文本 (居中)
$fontFile = __DIR__ . '/fonts/DejaVuSans-Bold.ttf'; // 需部署 TTF 字体
$box = imagettfbbox($fontSize, 0, $fontFile, $expr);
$textWidth = $box[2] - $box[0];
$textHeight = $box[1] - $box[7];
$x = ($width - $textWidth) / 2;
$y = ($height + $textHeight) / 2;
imagettftext($img, $fontSize, 0, $x, $y, $textColor, $fontFile, $expr);

// 边框
imagerectangle($img, 0, 0, $width-1, $height-1, imagecolorallocate($img, 100, 130, 160));

// 输出
imagepng($img);
imagedestroy($img);

安全加固清单

  1. 会话绑定:答案仅存 $_SESSION,不暴露于 Cookie/URL/隐藏域。
  2. 大小写不敏感校验:md5(strtoupper($input)) 兼容用户大小写习惯。
  3. 防暴力破解:同一 Session/IP 限制尝试次数 (如 5 次),失败锁定 15 分钟。
  4. 防重放攻击:校验成功后立即 unset($_SESSION['captcha_answer']),一次性失效。
  5. 随机性源:使用 random_int() (CSPRNG) 而非 rand()/mt_rand()。
  6. 图片不可缓存:HTTP 头 Cache-Control: no-store + URL 时间戳防浏览器缓存。
  7. 字体部署:TTF 字体文件随代码部署,避免系统字体缺失导致乱码。
  8. 算式多样性扩展:可扩展乘法 (1-9)、除法 (被除数为除数倍数)、混合运算,配置难度分级。

无障碍访问增强 (WCAG 2.1)

  • 提供「语音播放算式」按钮:<button onclick="speechSynthesis.speak(new SpeechSynthesisUtterance(''))">🔊</button>
  • 确保颜色对比度 ≥ 4.5:1 (WCAG AA),当前配色 7.2:1 通过。
  • alt 属性提示「点击刷新验证码」,而非泄露答案。

现代化替代方案对比

方案安全性用户体验实现成本适用场景
算术验证码 (本文)中 (可被专用 AI 破解)好 (无障碍友好)低 (纯 PHP+GD)中低风险表单、内部系统、学习演示
Google reCAPTCHA v3极高 (行为分析)极佳 (无感)低 (前端 JS + 后端验证)公开注册/登录/高价值表单
hCaptcha高 (图像分类)中 (需点选)低隐私敏感、中国大陆可用
滑块/拼图验证码中高 (轨迹分析)中 (移动端友好)中 (需前端 SDK)App/H5 原生应用

总结

算术验证码是「安全够用、体验友好、零依赖、易部署」的经典方案。核心要点:CSPRNG 随机性、Session 绑定一次性校验、GD 干扰绘制、无障碍增强。对于面向公网的高价值业务,强烈建议接入 reCAPTCHA v3/hCaptcha 等成熟风控服务,将验证码作为纵深防御的一环而非单一依赖。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved