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

PHP 圖片上傳與刪除完整實作:安全驗證、檔案管理與最佳實踐

     編輯:PHP 綜合知識

核心需求與安全考量

圖片上傳是 Web 開發高頻功能,但也是常見攻擊面。完整實作必須涵蓋:檔案類型驗證、檔名安全化、儲存路徑隔離、權限控制、錯誤處理。本文提供一個可直接用於生產環境的參考實作 (PHP 7.4+)。

上傳流程完整代碼

<?php
declare(strict_types=1);

class ImageUploader
{
    private const ALLOWED_MIME = [
        'image/jpeg' => '.jpg',
        'image/png'  => '.png',
        'image/gif'  => '.gif',
        'image/webp' => '.webp',
    ];
    private const MAX_SIZE = 5 * 1024 * 1024; // 5MB
    private const UPLOAD_DIR = __DIR__ . '/../storage/uploads/';

    public function upload(array $file): array
    {
        // 1. 基礎檢查
        if ($file['error'] !== UPLOAD_ERR_OK) {
            throw new RuntimeException('上傳錯誤代碼: ' . $file['error']);
        }
        if ($file['size'] > self::MAX_SIZE) {
            throw new RuntimeException('檔案過大,限制 ' . (self::MAX_SIZE/1024/1024) . 'MB');
        }

        // 2. MIME 類型驗證 (使用 finfo 避免偽造 $_FILES['type'])
        $finfo = new finfo(FILEINFO_MIME_TYPE);
        $mime = $finfo->file($file['tmp_name']);
        if (!isset(self::ALLOWED_MIME[$mime])) {
            throw new RuntimeException('不支援的檔案類型: ' . $mime);
        }
        $ext = self::ALLOWED_MIME[$mime];

        // 3. 進階內容驗證 (可選:getimagesize 檢查圖片完整性)
        $imgInfo = @getimagesize($file['tmp_name']);
        if ($imgInfo === false) {
            throw new RuntimeException('非有效圖片檔案或損壞');
        }

        // 4. 產生安全檔名 (時間戳 + 隨機字串 + 正確副檔名)
        $filename = sprintf(
            '%s_%s%s',
            date('YmdHis'),
            bin2hex(random_bytes(8)),
            $ext
        );
        $targetPath = self::UPLOAD_DIR . $filename;

        // 5. 確保目錄存在與權限
        if (!is_dir(self::UPLOAD_DIR)) {
            mkdir(self::UPLOAD_DIR, 0755, true);
        }

        // 6. 移動檔案 (原子操作)
        if (!move_uploaded_file($file['tmp_name'], $targetPath)) {
            throw new RuntimeException('檔案移動失敗,請檢查權限');
        }

        // 7. 設定檔案權限 (僅擁有者可讀寫)
        chmod($targetPath, 0644);

        return [
            'success' => true,
            'filename' => $filename,
            'path' => $targetPath,
            'url' => '/uploads/' . $filename, // 相對 Web 根目錄 URL
            'mime' => $mime,
            'size' => $file['size'],
            'width' => $imgInfo[0],
            'height' => $imgInfo[1],
        ];
    }

    public function delete(string $filename): bool
    {
        // 防止路徑遍歷攻擊
        $filename = basename($filename);
        $path = self::UPLOAD_DIR . $filename;
        
        if (!file_exists($path)) {
            return false; // 或拋出異常
        }
        
        return unlink($path);
    }
}

// 使用範例 (控制器中)
try {
    $uploader = new ImageUploader();
    $result = $uploader->upload($_FILES['img'] ?? []);
    // 將 $result['filename'] 存入資料庫
    echo json_encode(['code' => 0, 'data' => $result]);
} catch (Throwable $e) {
    http_response_code(400);
    echo json_encode(['code' => 1, 'msg' => $e->getMessage()]);
}

關鍵安全點深度解析

1. 不要信任 $_FILES['type']

瀏覽器根據副檔名猜測 MIME,極易偽造。必須使用 finfo 或 getimagesize 分析檔案實際內容。

2. 檔名安全化

  • 拒絕使用者原始檔名 (可能含 ../、特殊字元、中文編碼問題)
  • 採用 日期_隨機字串.副檔名 格式,避免衝突且不可預測
  • 副檔名由驗證後的 MIME 映射取得,非來自使用者輸入

3. 儲存目錄隔離與執行權限移除

  • 上傳目錄置於 public/webroot 之外 (如 storage/uploads),透過 PHP 輸出或 Nginx X-Accel-Redirect 讀取
  • 若必須置於 Web 目錄,Nginx/Apache 需設定 location ~ \.php$ { deny all; } 禁止腳本執行

4. 檔案大小與資源限制

同時配置 php.ini:

upload_max_filesize = 10M
post_max_size = 12M
max_file_uploads = 20
max_execution_time = 30
memory_limit = 128M

5. 刪除操作的路徑遍歷防護

basename() 移除目錄成分,確保只能刪除上傳目錄直屬檔案。

進階擴充建議

  1. 圖片處理管線:整合 intervention/image 自動產生縮圖、水印、格式轉 WebP。
  2. 雲端儲存:實作 StorageAdapter 介面,支援 S3、OSS、GCS 等,本地僅作暫存。
  3. 異步處理:大檔案/批次上傳丟入隊列,Worker 處理壓縮、掃毒、CDN 刷新。
  4. 掃毒整合:上傳後呼叫 ClamAV 或雲端安全 API 掃描惡意代碼。
  5. 資料庫關聯:建立 attachments 表記錄檔案元數據、關聯業務模型、軟刪除標記。

結語

安全的圖片上傳不僅是「移動檔案」,而是包含內容驗證、命名策略、儲存隔離、權限控制、錯誤處理的完整防禦體系。本文提供的 ImageUploader 類別遵循單一職責原則,易於測試與擴充,可作為專案媒體管理模組的基石。記住:永遠不要信任客戶端傳來的任何資訊。

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