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

PHP GD 圖形驗證碼生成完整實作與優化

     編輯:PHP 綜合知識

前言

圖形驗證碼(CAPTCHA)是防止機器人提交表單的基礎手段。PHP GD 擴充提供繪圖 API,可動態生成干擾線、噪點、扭曲文字。本文提供一個完整、可配置的驗證碼類,並說明安全性與無障礙考量。

驗證碼類實作

<?php
// Captcha.php
class Captcha {
    private int $width;
    private int $height;
    private int $length;
    private int $fontSize;
    private string $charset;
    private string $fontFile; // TTF 字體路徑,支援中文/特殊字型
    private array $bgColor;
    private array $textColorRange;
    private array $noiseColorRange;

    public function __construct(array $options = []) {
        $this->width         = $options['width'] ?? 120;
        $this->height        = $options['height'] ?? 40;
        $this->length        = $options['length'] ?? 4;
        $this->fontSize      = $options['font_size'] ?? 18;
        $this->charset       = $options['charset'] ?? '23456789ABCDEFGHJKLMNPQRSTUVWXYZ';
        $this->fontFile      = $options['font_file'] ?? '';
        $this->bgColor       = $options['bg_color'] ?? [255, 255, 255];
        $this->textColorRange= $options['text_color'] ?? [[50, 150], [50, 150], [50, 150]];
        $this->noiseColorRange= $options['noise_color'] ?? [[180, 230], [180, 230], [180, 230]];
    }

    public function generate(): string {
        $code = '';
        for ($i = 0; $i < $this->length; $i++) {
            $code .= $this->charset[random_int(0, strlen($this->charset) - 1)];
        }
        $_SESSION['captcha_code'] = strtoupper($code); // 儲存供驗證
        $this->outputImage($code);
        return $code;
    }

    public function verify(string $input): bool {
        $stored = $_SESSION['captcha_code'] ?? '';
        unset($_SESSION['captcha_code']); // 一次性
        return strtoupper(trim($input)) === $stored;
    }

    private function outputImage(string $code): void {
        $im = imagecreatetruecolor($this->width, $this->height);
        // 背景
        $bg = imagecolorallocate($im, ...$this->bgColor);
        imagefill($im, 0, 0, $bg);
        // 邊框
        $border = imagecolorallocate($im, 200, 200, 200);
        imagerectangle($im, 0, 0, $this->width - 1, $this->height - 1, $border);

        // 干擾線
        for ($i = 0; $i < 5; $i++) {
            $color = imagecolorallocate($im,
                random_int(...$this->noiseColorRange[0]),
                random_int(...$this->noiseColorRange[1]),
                random_int(...$this->noiseColorRange[2])
            );
            imageline($im,
                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($im,
                random_int(...$this->noiseColorRange[0]),
                random_int(...$this->noiseColorRange[1]),
                random_int(...$this->noiseColorRange[2])
            );
            imagesetpixel($im, random_int(0, $this->width-1), random_int(0, $this->height-1), $color);
        }

        // 文字
        $x = 10;
        for ($i = 0; $i < $this->length; $i++) {
            $char = $code[$i];
            $color = imagecolorallocate($im,
                random_int(...$this->textColorRange[0]),
                random_int(...$this->textColorRange[1]),
                random_int(...$this->textColorRange[2])
            );
            $y = random_int($this->fontSize + 2, $this->height - 4);
            $angle = random_int(-15, 15);

            if ($this->fontFile && file_exists($this->fontFile)) {
                // TTF 字體:支援抗鋸齒、中文、旋轉
                imagettftext($im, $this->fontSize, $angle, $x, $y, $color, $this->fontFile, $char);
                $box = imagettfbbox($this->fontSize, $angle, $this->fontFile, $char);
                $x += $box[2] - $box[0] + 2;
            } else {
                // 內建字體(僅限 ASCII、無旋轉)
                imagestring($im, 5, $x, $y - $this->fontSize, $char, $color);
                $x += imagefontwidth(5) + 2;
            }
        }

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

// 使用範例:captcha.php
session_start();
require_once 'Captcha.php';
$captcha = new Captcha([
    'font_file' => __DIR__ . '/fonts/DejaVuSans.ttf', // 需自備 TTF
    'length'    => 5,
    'width'     => 150,
    'height'    => 50,
]);
$captcha->generate();

前端整合

<form action="login.php" method="post">
  <img id="captcha-img" src="captcha.php" alt="驗證碼" title="點擊更換" style="cursor:pointer;vertical-align:middle">
  <input type="text" name="captcha" required autocomplete="off">
  <button type="submit">登入</button>
</form>
<script>
document.getElementById('captcha-img').addEventListener('click', function() {
    this.src = 'captcha.php?' + Date.now();
});
</script>

後端驗證

<?php
// login.php
session_start();
require_once 'Captcha.php';
$captcha = new Captcha();
if (!$captcha->verify($_POST['captcha'] ?? '')) {
    die('驗證碼錯誤');
}
// 繼續登入邏輯...
?>

安全性與無障礙建議

  • 使用 TTF 字體 + 旋轉 + 隨機位移:大幅提高 OCR 識別難度。
  • Session 一次性驗證:驗證後立即 unset,防重放。
  • 限制請求頻率:同一 IP 每分鐘生成上限,防暴力刷圖。
  • 無障礙替代方案:提供語音驗證碼(音檔播放數字)、或整合 hCaptcha/reCAPTCHA v3(行為分析,無需使用者輸入)。
  • HTTPS 傳輸:防止中間人攔截驗證碼圖片。

現代替代方案

方案優點缺點
Google reCAPTCHA v3 / hCaptcha無感驗證、極高安全性、免維護依賴第三方、隱私考量、中國大陸需特殊域名
Turnstile (Cloudflare)免費、隱私優先、支援隱私通道相對較新
自建 GD 驗證碼完全可控、無外部依賴、離線可用需自行對抗 OCR、無障礙需額外開發

結語

GD 生成驗證碼適合中小型專案或無法接入外部服務的環境。採用 TTF 字體、隨機干擾、Session 一次性驗證,可達到基本防機器人效果。長期演進建議評估無感驗證方案,提升使用者體驗。

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