Is uploaded file: примеры (PHP)

Проверка загрузки файлов через HTTP POST с is_uploaded_file
Раздел: Загрузка файлов
is_uploaded_file(string $filename): bool
Описание функции is_uploaded_file

Функция is_uploaded_file() в PHP проверяет, был ли указанный файл загружен через HTTP POST (например, через форму с атрибутом enctype="multipart/form-data"). Эта проверка является важной мерой безопасности, которая позволяет убедиться, что скрипт работает именно с загруженным пользователем файлом, а не с локальным файлом, путь к которому мог быть подделан.

Функция используется в процессе обработки загруженных файлов, обычно перед перемещением файла из временного каталога в постоянное место назначения с помощью функции move_uploaded_file().

Аргументы функции

Функция принимает один обязательный аргумент:

  • string $filename - путь к проверяемому файлу. Обычно это значение элемента $_FILES['userfile']['tmp_name'].

Возвращаемое значение: bool - true, если файл был загружен через HTTP POST, и false в противном случае.

Примеры использования is_uploaded_file
Базовый пример проверки одного файла
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $tmpFile = $_FILES['avatar']['tmp_name'];
    
    if (is_uploaded_file($tmpFile)) {
        echo "Файл был корректно загружен через POST.";
    } else {
        echo "Файл не был загружен через POST.";
    }
}
?>
// Если файл загружен через форму:
Файл был корректно загружен через POST.

// Если переменная $tmpFile указывает на локальный файл, например, '/etc/passwd':
Файл не был загружен через POST.
Пример в контексте полной обработки загрузки
<?php
if (isset($_FILES['document'])) {
    $uploadDir = 'uploads/';
    $tmpName = $_FILES['document']['tmp_name'];
    $finalName = $uploadDir . basename($_FILES['document']['name']);

    if (is_uploaded_file($tmpName)) {
        if (move_uploaded_file($tmpName, $finalName)) {
            echo "Файл успешно загружен и перемещен.";
        } else {
            echo "Ошибка перемещения файла.";
        }
    } else {
        echo "Возможная атака: файл не был загружен через POST.";
    }
}
?>
Похожие функции в PHP

move_uploaded_file() - не только проверяет, был ли файл загружен через POST, но и безопасно перемещает его в новое место. Это предпочтительная функция для финального сохранения загруженного файла, так как она включает в себя внутреннюю проверку is_uploaded_file(). Использовать is_uploaded_file() отдельно имеет смысл, если нужна только проверка без немедленного перемещения, либо для дополнительного логирования.

file_exists() - проверяет существование файла или каталога на диске, но не проверяет его происхождение. Она не является безопасной заменой, так как вернет true для любого существующего локального файла.

Аналоги в других языках программирования
Python (с использованием Flask)

В Python нет прямой аналогии, так как фреймворки сами управляют загруженными файлами. Файл становится доступен только после прохождения валидации фреймворком.

from flask import Flask, request, flash
import os

app = Flask(__name__)

@app.route('/upload', methods=['POST'])
def upload_file():
    if 'file' not in request.files:
        flash('No file part')
        return redirect(request.url)
    file = request.files['file']
    # Файл, полученный через request.files, уже является объектом загрузки
    if file.filename == '':
        flash('No selected file')
        return redirect(request.url)
    if file:
        filename = secure_filename(file.filename)
        file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
        return 'File uploaded successfully'
JavaScript (Node.js с Express и Multer)

В Node.js middleware Multer обрабатывает загрузку файлов, и проверка на «загруженность» осуществляется самим middleware до передачи файла в роут.

const express = require('express');
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });

const app = express();

app.post('/profile', upload.single('avatar'), (req, res) => {
    // Файл в req.file доступен только если Multer успешно его загрузил
    if (!req.file) {
        return res.status(400).send('No file uploaded.');
    }
    res.send('File uploaded!');
});

В отличие от PHP, где разработчик должен явно вызвать проверку, в этих языках и фреймворках валидация происхождения файла инкапсулирована в механизме обработки запросов.

Типичные ошибки
Проверка не временного имени файла
<?php
// ОШИБКА: Передается имя файла, а не временный путь.
if (is_uploaded_file($_FILES['file']['name'])) { // Неправильно
    // ...
}

// ПРАВИЛЬНО: Передается временный путь к файлу.
if (is_uploaded_file($_FILES['file']['tmp_name'])) { // Правильно
    // ...
}
?>
Игнорирование ошибок загрузки
<?php
// ОШИБКА: Проверка без предварительного анализа ошибки загрузки.
if (is_uploaded_file($_FILES['file']['tmp_name'])) {
    move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/file.txt');
}
// Если $_FILES['file']['error'] !== UPLOAD_ERR_OK, файл может быть не загружен.

// ПРАВИЛЬНО: Сначала проверяем код ошибки.
if ($_FILES['file']['error'] === UPLOAD_ERR_OK) {
    if (is_uploaded_file($_FILES['file']['tmp_name'])) {
        // ...
    }
}
?>
Проверка после перемещения файла
<?php
move_uploaded_file($tmpFile, $destination);
// ОШИБКА: Проверка после перемещения. Файла во временном каталоге уже нет.
if (is_uploaded_file($tmpFile)) { // Всегда false
    echo "Этот код никогда не выполнится.";
}
?>
Изменения в функции

Поведение функции is_uploaded_file() оставалось стабильным на протяжении многих версий PHP. В PHP 8.0 не было внесено значительных изменений, влияющих на ее работу. Основной принцип проверки файлов, загруженных через HTTP POST, не менялся с версии PHP 4.0.3. Всегда важно учитывать, что функция работает в связке с настройками php.ini, такими как file_uploads, upload_tmp_dir, upload_max_filesize и max_file_uploads.

Расширенные примеры
Проверка нескольких загруженных файлов
Пример php
<?php
if (!empty($_FILES['images']['name'][0])) {
    $uploadDir = 'uploads/';
    
    foreach ($_FILES['images']['tmp_name'] as $key => $tmpName) {
        // Проверяем каждый файл на ошибку загрузки
        if ($_FILES['images']['error'][$key] !== UPLOAD_ERR_OK) {
            echo "Ошибка загрузки файла {$_FILES['images']['name'][$key]}
"; continue; } // Проверяем, что файл действительно загружен через POST if (is_uploaded_file($tmpName)) { $safeName = $uploadDir . basename($_FILES['images']['name'][$key]); if (move_uploaded_file($tmpName, $safeName)) { echo "Файл {$_FILES['images']['name'][$key]} успешно сохранен.
"; } } else { echo "Файл {$_FILES['images']['name'][$key]} не прошел проверку безопасности.
"; } } } ?>
Логирование попыток небезопасного доступа
Пример php
<?php
$tmpFile = $_FILES['report']['tmp_name'];
$logFile = 'security.log';

if (!is_uploaded_file($tmpFile)) {
    $message = date('Y-m-d H:i:s') . 
               " | Попытка обработки не загруженного файла: " . 
               htmlspecialchars($tmpFile) . PHP_EOL;
    file_put_contents($logFile, $message, FILE_APPEND);
    die("Ошибка безопасности: недопустимый источник файла.");
}
// ... дальнейшая обработка безопасного файла
?>
Проверка перед выполнением дополнительных операций
Пример php
<?php
$tmpFile = $_FILES['config']['tmp_name'];

if (is_uploaded_file($tmpFile)) {
    // Можно безопасно получить MIME-тип для дополнительной валидации
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mime = finfo_file($finfo, $tmpFile);
    finfo_close($finfo);
    
    $allowedMimes = ['text/plain', 'application/json'];
    
    if (in_array($mime, $allowedMimes)) {
        // Чтение содержимого для анализа
        $content = file_get_contents($tmpFile);
        echo "Содержимое файла безопасно для обработки.";
    } else {
        echo "Запрещенный тип файла.";
    }
}
?>

PHP is_uploaded_file function comments

En
Is uploaded file Tells whether the file was uploaded via HTTP POST