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

PHP GD 庫生成驗證碼完整實作:從環境配置到防機器人策略

     編輯:PHP 相關知識

前言:驗證碼是表單防刷的第一道防線

GD 庫是 PHP 內建圖形處理擴展,本文提供生產級驗證碼實作:環境檢查、類別封裝、干擾線/點、Session 儲存、前端整合、無障礙考量與現代替代方案。

環境準備

# 檢查 GD 支援
php -m | grep gd
# 或
php -r "var_dump(gd_info());"

# Ubuntu/Debian 安裝
apt-get install php-gd
# CentOS/RHEL
yum install php-gd
# 重啟 Web Server
systemctl restart php-fpm nginx

驗證碼核心類別

<?php
namespace App\Captcha;

class Captcha {
    private $width  = 120;
    private $height = 40;
    private $length = 4;
    private $fontSize = 18;
    private $charset = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'; // 排除易混淆字元
    private $sessionKey = 'captcha_code';

    public function __construct(array $options = []) {
        foreach ($options as $k => $v) {
            if (property_exists($this, $k)) $this->$k = $v;
        }
    }

    public function generate(): string {
        $code = $this->generateCode();
        $this->saveToSession($code);
        $this->outputImage($code);
        return $code;
    }

    public function verify(string $input): bool {
        $stored = $_SESSION[$this->sessionKey] ?? '';
        unset($_SESSION[$this->sessionKey]); // 一次性驗證,防重放
        return strtoupper($input) === strtoupper($stored);
    }

    private function generateCode(): string {
        $code = '';
        $max = strlen($this->charset) - 1;
        for ($i = 0; $i < $this->length; $i++) {
            $code .= $this->charset[random_int(0, $max)];
        }
        return $code;
    }

    private function saveToSession(string $code): void {
        if (session_status() === PHP_SESSION_NONE) session_start();
        $_SESSION[$this->sessionKey] = $code;
    }

    private function outputImage(string $code): void {
        header('Content-Type: image/png');
        header('Cache-Control: no-store, no-cache, must-revalidate');
        header('Pragma: no-cache');

        $img = imagecreatetruecolor($this->width, $this->height);
        
        // 背景色
        $bgColor = imagecolorallocate($img, 245, 245, 245);
        imagefill($img, 0, 0, $bgColor);

        // 干擾線
        for ($i = 0; $i < 5; $i++) {
            $color = imagecolorallocate($img, random_int(180, 255), random_int(180, 255), random_int(180, 255));
            imageline($img, random_int(0, $this->width), random_int(0, $this->height), random_int(0, $this->width), random_int(0, $this->height), $color);
        }

        // 干擾點
        for ($i = 0; $i < 50; $i++) {
            $color = imagecolorallocate($img, random_int(0, 255), random_int(0, 255), random_int(0, 255));
            imagesetpixel($img, random_int(0, $this->width), random_int(0, $this->height), $color);
        }

        // 繪製文字
        $x = 10;
        for ($i = 0; $i < $this->length; $i++) {
            $color = imagecolorallocate($img, random_int(0, 100), random_int(0, 150), random_int(0, 200));
            $angle = random_int(-15, 15);
            $y = random_int($this->height / 2, $this->height - 5);
            imagettftext($img, $this->fontSize, $angle, $x, $y, $color, __DIR__ . '/fonts/arial.ttf', $code[$i]);
            $x += $this->fontSize + 2;
        }

        imagepng($img);
        imagedestroy($img);
    }
}

前端整合範例

<!-- HTML -->
<form action="/login" method="POST">
    <input type="text" name="username" placeholder="帳號" required>
    <input type="password" name="password" placeholder="密碼" required>
    <div class="captcha-group">
        <input type="text" name="captcha" placeholder="驗證碼" required style="width: 80px">
        <img id="captchaImg" src="/captcha" alt="驗證碼" style="vertical-align: middle; cursor: pointer" title="點擊更換">
    </div>
    <button type="submit">登入</button>
</form>

<script>
document.getElementById('captchaImg').addEventListener('click', function() {
    this.src = '/captcha?' + Date.now(); // 防快取
});
</script>

後端路由處理

<?php
// /captcha 路由
$captcha = new App\Captcha\Captcha([
    'width' => 130,
    'height' => 45,
    'length' => 4,
]);
$captcha->generate(); // 直接輸出圖片

// /login 路由
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $captcha = new App\Captcha\Captcha();
    if (!$captcha->verify($_POST['captcha'] ?? '')) {
        die('驗證碼錯誤');
    }
    // 後續登入邏輯...
}

無障礙與安全增強

措施說明
音頻驗證碼為視障用戶提供語音播放選項(需 TTS 服務)
限制嘗試次數同一 IP/Session 5 分鐘內失敗 5 次鎖定 15 分鐘
HTTPS 傳輸防止驗證碼圖片被中間人竊取分析
字體隨機化準備多種 TTF 字體,每次隨機選擇
背景噪點算法使用柏林噪音、正弦波紋理替代隨機點

現代替代方案:reCAPTCHA v3 / hCaptcha / Turnstile

  • 無感驗證(行為分析),用戶體驗最佳
  • 後端驗證:file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=$secret&response=$token")
  • 適合公開表單(註冊、留言、密碼重設)

結語

GD 生成驗證碼適合內部系統、低流量表單、無外網環境。公開面向用戶的關鍵表單,建議遷移至 reCAPTCHA v3 / hCaptcha / Turnstile 等無感驗證服務,兼顧安全與體驗。

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