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

PHP 安全檔案上傳:驗證 MIME 類型、限制副檔名、防止腳本執行

     編輯:PHP 相關知識

PHP 安全檔案上傳:驗證 MIME 類型、限制副檔名、防止腳本執行

檔案上傳是 Web 應用高風險功能,處理不當易導致惡意腳本上傳、任意程式碼執行。本文提供一套完整的安全上傳流程:MIME 類型驗證、副檔名白名單、檔案重命名、儲存目錄權限控制、防止直接存取執行。

完整上傳處理腳本

<?php
// upload.php
$uploadDir = __DIR__ . '/uploads/';
$allowedMimes = [
    'image/jpeg' => 'jpg',
    'image/png'  => 'png',
    'image/gif'  => 'gif',
    'image/webp' => 'webp',
];
$maxSize = 5 * 1024 * 1024; // 5MB

// 確保上傳目錄存在且不可執行 PHP
if (!is_dir($uploadDir)) {
    mkdir($uploadDir, 0755, true);
    // 建立 .htaccess 禁止腳本執行(Apache)
    file_put_contents($uploadDir . '.htaccess', "SetHandler default-handler\nOptions -ExecCGI\nphp_flag engine off\n");
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
    $file = $_FILES['file'];

    // 1. 檢查上傳錯誤
    if ($file['error'] !== UPLOAD_ERR_OK) {
        die('上傳錯誤代碼: ' . $file['error']);
    }

    // 2. 檢查檔案大小
    if ($file['size'] > $maxSize) {
        die('檔案過大,限制 ' . ($maxSize / 1024 / 1024) . 'MB');
    }

    // 3. 偵測真實 MIME 類型(使用 finfo,不可信任 $_FILES['type'])
    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $mime = $finfo->file($file['tmp_name']);
    if (!isset($allowedMimes[$mime])) {
        die('不支援的檔案類型: ' . $mime);
    }

    // 4. 產生安全檔名:隨機字串 + 正確副檔名
    $ext = $allowedMimes[$mime];
    $safeName = bin2hex(random_bytes(16)) . '.' . $ext;
    $destPath = $uploadDir . $safeName;

    // 5. 移動檔案
    if (!move_uploaded_file($file['tmp_name'], $destPath)) {
        die('檔案移動失敗');
    }

    // 6. 設定檔案權限(僅讀)
    chmod($destPath, 0644);

    // 成功回應
    echo json_encode([
        'success' => true,
        'filename' => $safeName,
        'original' => $file['name'],
        'url' => '/uploads/' . $safeName,
    ]);
    exit;
}
?>
<!DOCTYPE html>
<html>
<head><title>安全上傳示範</title></head>
<body>
<form method="post" enctype="multipart/form-data">
    <input type="file" name="file" accept="image/*" required>
    <button type="submit">上傳</button>
</form>
</body>
</html>

關鍵安全措施說明

措施說明
使用 finfo 偵測 MIME依檔案內容判斷類型,防止偽造 $_FILES['type']
副檔名白名單映射僅允許已知安全的副檔名,拒絕 .php, .phtml, .php5 等
隨機檔名避免檔名衝突、路徑遍歷、使用者推測檔案位置
儲存目錄禁用執行Apache 用 .htaccess php_flag engine off;Nginx 設定 location ~ \.php$ { deny all; }
檔案權限 0644移除執行權限,降低風險
限制上傳大小防止 DoS 佔滿磁碟,配合 php.ini upload_max_filesize, post_max_size

進階:圖片二次渲染(徹底移除惡意內容)

<?php
// 上傳為圖片後,用 GD 重新繪製儲存,可剝離夾帶的 PHP 代碼
$img = imagecreatefromstring(file_get_contents($destPath));
if ($img !== false) {
    imagejpeg($img, $destPath, 90); // 重寫為純淨 JPEG
    imagedestroy($img);
}
?>

Nginx 設定範例(禁止上傳目錄執行 PHP)

location /uploads/ {
    # 禁止執行任何腳本
    location ~ \.php$ {
        return 403;
    }
    # 禁止解析雙副檔名
    location ~ \.(php|phtml|php5)\. {
        return 403;
    }
}

總結

安全上傳的核心原則:不信任客戶端任何資訊(檔名、MIME、大小),伺服器端完整驗證,儲存與執行環境隔離。上述措施疊加使用,可有效阻擋 99% 以上的上傳攻擊。

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