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

PHP 驗證碼生成驗證完整實現:GD 库進階應用

     編輯:PHP 相關知識

PHP 驗證碼生成驗證完整實現:GD 库進階應用

驗證碼是表單防刷基礎設施。本文實現功能完整的驗證碼類,支援多字符集、TTF 字體、幹擾線/點/曲線、Session 存儲、AJAX 刷新、無障礙語音驗證碼接口,安全易用。

核心驗證碼類

config = array_merge([
            'width' => 130,
            'height' => 50,
            'length' => 4,
            'charset' => '23456789ABCDEFGHJKLMNPQRSTUVWXYZ',  // 排除易混淆字符
            'fontSize' => 24,
            'fontFile' => __DIR__ . '/fonts/DejaVuSans-Bold.ttf',  // 需提供 TTF
            'bgColor' => [240, 240, 240],
            'useCurve' => true,      // 貝塞爾曲線干擾
            'useNoise' => true,      // 噪點
            'useLines' => true,      // 干擾線
            'sessionKey' => 'captcha_code',
            'expire' => 600,         // 10分鐘過期
        ], $config);
    }
    
    public function generate() {
        session_start();
        
        // 1. 創建畫布
        $this->image = imagecreatetruecolor($this->config['width'], $this->config['height']);
        
        // 2. 背景填充
        $bg = imagecolorallocate($this->image, ...$this->config['bgColor']);
        imagefill($this->image, 0, 0, $bg);
        
        // 3. 幹擾元素 (先繪製,字符在上層)
        if ($this->config['useNoise']) $this->drawNoise(80);
        if ($this->config['useLines']) $this->drawLines(4);
        if ($this->config['useCurve']) $this->drawBezierCurve(2);
        
        // 4. 生成字符並繪製
        $this->code = $this->generateCode();
        $this->drawChars();
        
        // 5. 存入 Session (小寫統一驗證)
        $_SESSION[$this->config['sessionKey']] = strtolower($this->code);
        $_SESSION[$this->config['sessionKey'] . '_time'] = time();
        
        // 6. 輸出圖片
        header('Content-Type: image/png');
        header('Cache-Control: no-store, no-cache, must-revalidate');
        header('Pragma: no-cache');
        imagepng($this->image);
        imagedestroy($this->image);
        
        return $this->code;
    }
    
    private function generateCode(): string {
        $len = strlen($this->config['charset']);
        $code = '';
        for ($i = 0; $i < $this->config['length']; $i++) {
            $code .= $this->config['charset'][random_int(0, $len - 1)];
        }
        return $code;
    }
    
    private function drawChars() {
        $width = $this->config['width'];
        $height = $this->config['height'];
        $length = $this->config['length'];
        $fontSize = $this->config['fontSize'];
        $fontFile = $this->config['fontFile'];
        
        $charWidth = ($width - 20) / $length;
        $startX = 10;
        
        for ($i = 0; $i < $length; $i++) {
            $char = $this->code[$i];
            $x = $startX + $i * $charWidth + random_int(-2, 2);
            $y = random_int($height * 0.4, $height * 0.7);
            $angle = random_int(-15, 15);
            
            $color = imagecolorallocate($this->image, 
                random_int(30, 120), random_int(30, 120), random_int(30, 120));
            
            // TTF 字體繪製 (需 GD 支援 FreeType)
            if (file_exists($fontFile) && function_exists('imagettftext')) {
                imagettftext($this->image, $fontSize, $angle, $x, $y, $color, $fontFile, $char);
            } else {
                // 降級內置字體
                $color = imagecolorallocate($this->image, 0, 0, 0);
                imagestring($this->image, 5, $x, $y - 10, $char, $color);
            }
        }
    }
    
    private function drawNoise(int $count) {
        for ($i = 0; $i < $count; $i++) {
            $color = imagecolorallocate($this->image, 
                random_int(150, 255), random_int(150, 255), random_int(150, 255));
            imagesetpixel($this->image, random_int(0, $this->config['width']), random_int(0, $this->config['height']), $color);
        }
    }
    
    private function drawLines(int $count) {
        for ($i = 0; $i < $count; $i++) {
            $color = imagecolorallocate($this->image, 
                random_int(100, 200), random_int(100, 200), random_int(100, 200));
            imageline($this->image, 
                random_int(0, $this->config['width']), random_int(0, $this->config['height']),
                random_int(0, $this->config['width']), random_int(0, $this->config['height']),
                $color);
        }
    }
    
    private function drawBezierCurve(int $count) {
        for ($i = 0; $i < $count; $i++) {
            $color = imagecolorallocate($this->image, 
                random_int(80, 180), random_int(80, 180), random_int(80, 180));
            
            // 三次貝塞爾曲線: 起點、控制點1、控制點2、終點
            $points = [
                random_int(0, $this->config['width'] * 0.2),
                random_int(0, $this->config['height']),
                random_int($this->config['width'] * 0.3, $this->config['width'] * 0.7),
                random_int(0, $this->config['height']),
                random_int($this->config['width'] * 0.3, $this->config['width'] * 0.7),
                random_int(0, $this->config['height']),
                random_int($this->config['width'] * 0.8, $this->config['width']),
                random_int(0, $this->config['height']),
            ];
            
            // 近似繪製: 分段連線
            $segments = 20;
            for ($t = 0; $t <= $segments; $t++) {
                $t /= $segments;
                $x = pow(1-$t,3)*$points[0] + 3*pow(1-$t,2)*$t*$points[2] + 3*(1-$t)*pow($t,2)*$points[4] + pow($t,3)*$points[6];
                $y = pow(1-$t,3)*$points[1] + 3*pow(1-$t,2)*$t*$points[3] + 3*(1-$t)*pow($t,2)*$points[5] + pow($t,3)*$points[7];
                if ($t > 0) imageline($this->image, $lastX, $lastY, $x, $y, $color);
                $lastX = $x; $lastY = $y;
            }
        }
    }
    
    public static function verify(string $input, string $sessionKey = 'captcha_code'): bool {
        session_start();
        $stored = strtolower($_SESSION[$sessionKey] ?? '');
        $time = $_SESSION[$sessionKey . '_time'] ?? 0;
        
        // 過期檢查
        if (time() - $time > 600) return false;
        
        // 一次性驗證 (驗證後立即銷毀)
        $valid = $stored !== '' && strtolower($input) === $stored;
        if ($valid) {
            unset($_SESSION[$sessionKey], $_SESSION[$sessionKey . '_time']);
        }
        return $valid;
    }
    
    // 無障礙: 語音驗證碼接口
    public function audio(): string {
        session_start();
        $code = $_SESSION[$this->config['sessionKey']] ?? '';
        if (!$code) return '';
        
        // 簡單實現: 數字/字母逐個發音 (需 TTS 服務)
        // 這裡返回 Base64 編碼的 WAV 數據 (示例)
        // 實際項目建議接入百度/騰訊/阿里雲 TTS API
        return base64_encode($this->generateAudioWave($code));
    }
    
    private function generateAudioWave(string $text): string {
        // 生成簡單正弦波 WAV (教學用,實際不可用)
        // 實際項目請使用專業 TTS
        return '';
    }
}

使用示例

 __DIR__ . '/fonts/DejaVuSans-Bold.ttf',
]);
$captcha->generate();

// 2. 前端頁面
?>


聽不清?語音驗證



audio());

進階安全加固

威脅防禦措施
OCR 識別貝塞爾曲線干擾、字符旋转/扭曲、顏色干擾、字符粘連
Session 劫持HTTPS 傳輸、Session ID 定期重生成、HttpOnly Cookie
暴力嘗試失敗計數鎖定 (Redis/IP+UA)、驗證碼一次性銷毀
繞過驗證服務端強制驗證、Token 機制雙重保障
自動化腳本行為分析 (滑軌/點擊/時間)、設備指紋、風控引擎

現代替代方案對比

方案安全性用戶體驗實現成本適用場景
圖形驗證碼中差低低風險表單、內部系統
滑塊/點選驗證高好中 (需第三方 SDK)登錄/註冊/關鍵操作
短信/郵箱驗證碼高中高 (通訊費用)實名認證/支付/找回密碼
行為驗證 (無感)極高極好高 (需風控平台)高價值業務/大規模防刷
設備指紋 + 風控極高無感極高金融/電商核心交易

圖形驗證碼雖非完美,但在成本與安全平衡點上仍是中小項目主流選擇。正確實現 GD 進階繪圖、一次性驗證、無障礙支援,能將破解成本提升 100 倍以上。

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