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

PHP 實現算術驗證碼:生成「數字+運算符」圖片驗證的完整代碼

     編輯:PHP 綜合知識

為什麼選擇算術驗證碼?

  • **用戶友好**:無需辨認扭曲字符,心算即可通過。
  • **安全性足夠**:機器需解析圖片→OCR→計算,成本遠高於純文字驗證碼。
  • **無障礙**:屏幕閱讀器可直接朗讀算式(配合 alt 屬性)。

核心邏輯

  1. 隨機生成 2-3 個個位數 + 隨機運算符(+ - *)
  2. 計算真實結果 → md5(strtoupper(結果)) 存入 Session
  3. 將算式文本(如 3+5-2)繪製到 GD 圖片
  4. 前端提交用戶輸入 → md5(strtoupper(輸入)) 與 Session 比對

完整代碼(兩文件)

1. captcha.php — 圖片生成器

 $n) {
    $expr .= $n;
    if (isset($ops[$i])) $expr .= $ops[$i];
}
$result = eval("return $expr;"); // 僅含數字和 + - *,安全

// 存入 Session(大寫統一,防大小寫干擾)
$_SESSION[$action] = md5(strtoupper((string)$result));

// 2. 繪製圖片
header('Content-Type: image/png');
$im = imagecreatetruecolor($imgW, $imgH);

// 顏色分配
$white = imagecolorallocate($im, 255, 255, 255);
$gray  = imagecolorallocate($im, 220, 220, 220);
$black = imagecolorallocate($im, 0, 0, 0);
$blue  = imagecolorallocate($im, 50, 50, 200);

// 填充背景
imagefill($im, 0, 0, $white);

// 干擾像素
for ($i = 0; $i < 800; $i++) {
    imagesetpixel($im, random_int(0, $imgW), random_int(0, $imgH), $gray);
}

// 干擾線
for ($i = 0; $i < 10; $i++) {
    imageline($im, 0, random_int(0, $imgH), $imgW, random_int(0, $imgH), $gray);
    imageline($im, random_int(0, $imgW), 0, random_int(0, $imgW), $imgH, $gray);
}

// 繪製算式文本(使用 TTF 字體,需確保路徑正確)
$font = __DIR__ . '/fonts/DejaVuSans.ttf'; // 建議放項目內
$fontSize = min($imgW, $imgH) * 0.45;
$text = implode(' ', array_map(null, $nums, array_merge($ops, ['']))) . ' = ?';
// 簡單拼接:3 + 5 - 2 = ?
// 更精確居中需用 imagettfbbox 計算邊界框
$x = 10;
$y = $imgH * 0.65;
imagettftext($im, $fontSize, 0, $x, $y, $black, $font, $text);

// 輸出
imagepng($im);
imagedestroy($im);

2. verify.php — 表單處理與驗證




算術驗證碼演示

算術驗證碼

進階強化建議

維度方案
運算難度可配置位數(1-99)、運算符集合(+ - * /)、括號優先級
防 OCR字體隨機旋轉(-15°~15°)、字符隨機顏色、波浪扭曲、噪點密度自適應
無障礙<img alt="3 + 5 - 2 = ?"> + 提供「語音播放」按鈕(Web Speech API)
分佈式Session 改用 Redis,Key 設 TTL 5 分鐘,Key 格式 captcha:{action}:{session_id}
防暴力同 IP/Session 5 分鐘內失敗 > 5 次暫時封禁,記錄日誌告警
字體版權使用開源字體(DejaVu、Noto Sans、Source Han Sans)避免侵權

常見坑與修正

  • eval() 風險:示例中 $expr 完全由程式生成(純數字+運算符),安全。若允許用戶自定義運算符,**嚴禁**用 eval(),改用自寫解析器或 symfony/expression-language。
  • Session 未啟動:兩文件頂部必須 session_start(),且無輸出前調用。
  • 字體路徑:生產環境用絕對路徑 __DIR__ . '/fonts/...',確保 Web 進程可讀。
  • 大小寫:統一 strtoupper() 再 md5(),避免用戶輸入「6」vs「6」之類不可见字符差異。
  • 緩存:圖片響應頭加 Cache-Control: no-store, must-revalidate,防止瀏覽器緩存導致驗證碼不更新。

一鍵封裝類(可直接 Composer 安裝)

// src/Captcha/MathCaptcha.php
namespace App\Captcha;

class MathCaptcha {
    private $action;
    private $width, $height;
    private $fontPath;

    public function __construct(string $action, int $w = 120, int $h = 40, string $font = '') {
        $this->action = $action;
        $this->width = $w; $this->height = $h;
        $this->fontPath = $font ?: __DIR__ . '/../fonts/DejaVuSans.ttf';
    }

    public function render(): void {
        if (session_status() !== PHP_SESSION_ACTIVE) session_start();
        // ... 生成算式、存 Session、輸出圖片(同上文邏輯)
    }

    public function verify(string $input): bool {
        if (session_status() !== PHP_SESSION_ACTIVE) session_start();
        $hash = $_SESSION[$this->action] ?? '';
        unset($_SESSION[$this->action]); // 一次性
        return hash_equals($hash, md5(strtoupper(trim($input))));
    }
}

// 使用
$captcha = new MathCaptcha('login');
if ($_GET['captcha'] === '1') { $captcha->render(); exit; }

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if ($captcha->verify($_POST['captcha'] ?? '')) {
        echo 'OK';
    } else {
        echo 'FAIL';
    }
}

總結

算術驗證碼以 極低開發成本 兼顧 **安全性、易用性、無障礙**,是中小型項目替換傳統字符驗證碼的理想選擇。核心只有 50 行代碼,按需擴展即可投入生產。

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