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

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

     編輯:PHP 相關知識

驗證碼是防機器人基本防線。本文提供兩種 GD 實作:簡易版與進階版,並涵蓋 Session 驗證、無障礙、安全性考量。

前置檢查:確認 GD 擴充

<?php
if (!extension_loaded('gd')) {
    die('GD 未安裝:請安裝 php-gd 並重啟 PHP-FPM/Apache');
}
$gdInfo = gd_info();
echo "GD 版本: {$gdInfo['GD Version']}\n";
echo "FreeType: " . ($gdInfo['FreeType Support'] ? '是' : '否') . "\n";
echo "JPEG: " . ($gdInfo['JPEG Support'] ? '是' : '否') . "\n";
echo "PNG: " . ($gdInfo['PNG Support'] ? '是' : '否') . "\n";

版本一:簡易數字/字母驗證碼

<?php
session_start();

function generateCaptcha(int $length = 4, int $width = 120, int $height = 40): void
{
    // 字元集:排除易混淆字元 0/O, 1/I/l
    $chars = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ';
    $code = '';
    for ($i = 0; $i < $length; $i++) {
        $code .= $chars[random_int(0, strlen($chars) - 1)];
    }
    $_SESSION['captcha_code'] = $code;

    // 建立畫布
    $img = imagecreatetruecolor($width, $height);
    
    // 顏色分配
    $bgColor = imagecolorallocate($img, 245, 245, 245);
    $borderColor = imagecolorallocate($img, 180, 180, 180);
    $textColor = imagecolorallocate($img, random_int(0, 100), random_int(0, 80), random_int(0, 80));
    $noiseColor = imagecolorallocate($img, 200, 200, 200);

    // 背景與邊框
    imagefilledrectangle($img, 0, 0, $width, $height, $bgColor);
    imagerectangle($img, 0, 0, $width - 1, $height - 1, $borderColor);

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

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

    // 繪製文字(需 FreeType 支援中文/TTF)
    $fontSize = $height * 0.6;
    $x = 10;
    $y = $height - ($height - $fontSize) / 2;
    
    if ($gdInfo['FreeType Support'] && file_exists(__DIR__ . '/fonts/DejaVuSans.ttf')) {
        imagettftext($img, $fontSize, 0, $x, $y, $textColor, __DIR__ . '/fonts/DejaVuSans.ttf', $code);
    } else {
        // 內建字體備援(僅支援拉丁字母、固定大小)
        imagestring($img, 5, $x, $y - 8, $code, $textColor);
    }

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

generateCaptcha();

版本二:進階彎曲/扭曲驗證碼(更強抗識別)

<?php
session_start();

function generateAdvancedCaptcha(int $length = 5): void
{
    $chars = '346789ABCDEFGHKLMNPRTUVWXY';
    $code = '';
    for ($i = 0; $i < $length; $i++) {
        $code .= $chars[random_int(0, strlen($chars) - 1)];
    }
    $_SESSION['captcha_code'] = strtoupper($code); // 不分大小寫驗證

    $width = 160;
    $height = 50;
    $img = imagecreatetruecolor($width, $height);

    // 漸層背景
    for ($y = 0; $y < $height; $y++) {
        $r = 230 + (int)($y * 20 / $height);
        $g = 240 + (int)($y * 15 / $height);
        $b = 250;
        $color = imagecolorallocate($img, $r, $g, $b);
        imageline($img, 0, $y, $width, $y, $color);
    }

    // 正弦波干擾線
    for ($i = 0; $i < 3; $i++) {
        $color = imagecolorallocate($img, random_int(100, 200), random_int(100, 200), random_int(100, 200));
        $amplitude = random_int(5, 15);
        $period = random_int(30, 60);
        $phase = random_int(0, $width);
        for ($x = 0; $x < $width; $x++) {
            $y = $height / 2 + $amplitude * sin(($x + $phase) * 2 * M_PI / $period);
            imagesetpixel($img, $x, (int)$y, $color);
        }
    }

    // 字元逐個繪製,隨機旋轉、位移
    $fontFile = __DIR__ . '/fonts/DejaVuSans-Bold.ttf';
    $fontSize = 24;
    $x = 15;
    for ($i = 0; $i < $length; $i++) {
        $angle = random_int(-15, 15);
        $y = random_int(30, 40);
        $color = imagecolorallocate($img, random_int(0, 80), random_int(0, 80), random_int(0, 80));
        imagettftext($img, $fontSize, $angle, $x, $y, $color, $fontFile, $code[$i]);
        $x += $fontSize + random_int(2, 8);
    }

    header('Content-Type: image/png');
    header('Cache-Control: no-store, no-cache, must-revalidate');
    imagepng($img);
    imagedestroy($img);
}

前端驗證整合

<!-- HTML -->
<img id="captcha" src="captcha.php" alt="驗證碼" title="點擊更換" style="cursor:pointer;vertical-align:middle">
<input type="text" name="captcha_input" maxlength="6" required>

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

後端驗證

<?php
session_start();

function verifyCaptcha(string $input): bool
{
    if (empty($_SESSION['captcha_code'])) return false;
    $valid = strtoupper(trim($input)) === $_SESSION['captcha_code'];
    // 一次性驗證,防止重放
    unset($_SESSION['captcha_code']);
    return $valid;
}

// 使用
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!verifyCaptcha($_POST['captcha_input'] ?? '')) {
        die('驗證碼錯誤');
    }
    // 正常業務邏輯...
}

現代替代方案建議

方案優點缺點
Google reCAPTCHA v3無感驗證、極強安全性需網路、隱私考量、中國大陸需備案
hCaptcha隱私友善、支援中國需網路、體驗稍差
Turnstile (Cloudflare)免費、無感、隱私優先較新、相容性待驗證
滑動/點選驗證體驗好、移動端友善需前端 SDK、維護成本高
時間蜜罐零干擾、簡單只擋低級爬蟲

安全性檢查清單

  • ☑ 使用 random_int() 而非 mt_rand()(加密級隨機)
  • ☑ Session 設定 httponly、secure、samesite=Strict
  • ☑ 驗證後立即銷毀 Session 驗證碼(一次性)
  • ☑ 限制嘗試次數(如 5 次失效)
  • ☑ 圖片回應設定 Cache-Control: no-store
  • ☑ 考慮加入語音驗證碼(無障礙)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved