Загрузка файлов и валидация в PHP
Браузер отправляет файл в теле запроса multipart/form-data. PHP кладёт метаданные в $_FILES, тело — во временный каталог. Дальше разработчик проверяет файл и переносит его в хранилище через move_uploaded_file().
В прикладной разработке загрузка файла почти всегда состоит из двух частей: техническая приемка файла и бизнес-правила (кто может загружать, какие типы разрешены, сколько хранить, когда удалять).
См. также данные со страницы, суперглобальные массивы — $_FILES, безопасные пути при работе с файлами, Laravel — Form Request.
Форма и лимиты сервера
<form method="POST" action="/upload.php" enctype="multipart/form-data">
<input type="file" name="document" accept=".pdf,application/pdf" required />
<button type="submit">Загрузить</button>
</form>
Настройка php.ini | Назначение |
|---|---|
upload_max_filesize | Максимальный размер одного файла |
post_max_size | Лимит всего POST (должен быть ≥ суммы полей и файлов) |
max_file_uploads | Сколько файлов за один запрос |
file_uploads | Включена ли загрузка (On / Off) |
Код ошибки в $_FILES['поле']['error'] — константы UPLOAD_ERR_OK, UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE и др. Всегда проверяйте error === UPLOAD_ERR_OK до любой обработки содержимого.
Структура $_FILES
| Ключ | Откуда берётся | Можно ли доверять |
|---|---|---|
name | Имя на компьютере пользователя | Только для отображения; в пути на диске — своё имя |
type | Заголовок от браузера | Подделывается |
size | Размер в байтах | Ориентир; дополнительно проверяйте на диске |
tmp_name | Путь к временному файлу на сервере | Используйте с is_uploaded_file() |
error | Код результата загрузки | Обязателен к проверке |
Подробнее о суперглобальных — 154.md.
Что проверять (слои защиты)
| Шаг | Действие |
|---|---|
| 1 | REQUEST_METHOD === 'POST', поле присутствует, error === UPLOAD_ERR_OK |
| 2 | Лимит размера в коде (меньше или равен политике приложения) |
| 3 | MIME по содержимому — finfo_file() / класс finfo, не $_FILES['type'] |
| 4 | Расширение — вспомогательный признак, не единственный |
| 5 | is_uploaded_file($tmp) перед move_uploaded_file() |
| 6 | Сохранение под случайным именем; оригинал — в БД при необходимости |
| 7 | Каталог вне public/ или раздача через контроллер, не прямой URL на uploads/evil.php |
Переименованный malware.exe → malware.pdf пройдёт проверку только по расширению или по type из формы. Содержимое останется исполняемым.
Поэтому надежная политика строится как набор одновременно работающих ограничений, а не как одна "волшебная" проверка.
Валидация на чистом PHP
<?php
declare(strict_types=1);
function detectMime(string $path): string
{
$finfo = new finfo(FILEINFO_MIME_TYPE);
return $finfo->file($path) ?: 'application/octet-stream';
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
exit('Method Not Allowed');
}
$file = $_FILES['document'] ?? null;
if ($file === null || $file['error'] !== UPLOAD_ERR_OK) {
http_response_code(400);
exit('Ошибка загрузки');
}
$tmp = $file['tmp_name'];
$maxBytes = 5 * 1024 * 1024;
$allowedMimes = ['application/pdf' => 'pdf'];
if ($file['size'] > $maxBytes || !is_uploaded_file($tmp)) {
http_response_code(400);
exit('Файл отклонён');
}
$mime = detectMime($tmp);
if (!isset($allowedMimes[$mime])) {
http_response_code(400);
exit('Допустим только PDF');
}
$extension = $allowedMimes[$mime];
$storedName = bin2hex(random_bytes(16)) . '.' . $extension;
$uploadDir = __DIR__ . '/../var/uploads';
$target = $uploadDir . '/' . $storedName;
if (!is_dir($uploadDir) && !mkdir($uploadDir, 0750, true)) {
http_response_code(500);
exit('Каталог недоступен');
}
if (!move_uploaded_file($tmp, $target)) {
http_response_code(500);
exit('Не удалось сохранить файл');
}
// $file['name'] — только для UI или записи в БД как "оригинальное имя"
Для изображений дополнительно имеет смысл getimagesize() — убедиться, что файл действительно декодируется как картинка.
Расширение и MIME — в чём разница
| Проверка | Что смотрит | Риск |
|---|---|---|
Расширение в имени (report.pdf) | Суффикс строки name | Переименование любого файла |
$_FILES['type'] | Заголовок от клиента | Подделка в DevTools |
finfo / mime в фреймворке | Сигнатура байтов на сервере | Надёжнее; для PDF иногда нужны доп. проверки (парсер, антивирус) |
Политика «только PDF» = MIME по содержимому + ограничение размера + безопасное хранение. Расширение согласуют с MIME, а не подменяют им.
Laravel — extensions, mimes и file
В Laravel валидация в $request->validate() или Form Request:
// Слабо: только суффикс имени
$request->validate([
'document' => 'required|extensions:pdf',
]);
// malware.exe, переименованный в malware.pdf → проходит
// Надёжнее: загруженный файл + MIME + расширение
$request->validate([
'document' => 'required|file|extensions:pdf|mimes:pdf',
]);
// тот же переименованный exe → отклоняется
| Правило | Смысл |
|---|---|
file | Поле — реальная загрузка через HTTP |
extensions:pdf | Суффикс имени в списке |
mimes:pdf | MIME по содержимому (как у finfo) |
max:5120 | Размер в килобайтах |
image, dimensions | Дополнительно для картинок |
Сохранение — $request->file('document')->store('documents') или диск Storage. Каталог storage/app по умолчанию недоступен из веба; публичная раздача — через php artisan storage:link и осознанный выбор файлов.
Form Request с файлом:
public function rules(): array
{
return [
'document' => ['required', 'file', 'mimes:pdf', 'max:5120'],
];
}
Symfony (кратко)
В Symfony 6+ для загрузок используют ограничение File / Image в валидаторе: maxSize, mimeTypes, extensions. Логика та же — проверка на сервере по содержимому и размеру, не по доверию к имени файла. Обзор фреймворка — 144.md.
Типовые ошибки
| Ошибка | Последствие |
|---|---|
Сохранять под оригинальным name в public/uploads | Path traversal, выполнение .php |
Доверять только accept в HTML | Атрибут только подсказка браузеру |
Пропустить is_uploaded_file | Запись произвольного пути с диска |
| Хранить исполняемые типы в каталоге, откуда веб-сервер исполняет PHP | RCE при обходе фильтра |
Частая архитектурная ошибка: считать загрузку "завершенной" сразу после move_uploaded_file(). Обычно нужен второй шаг:
- запись метаданных в БД (
owner_id,mime,size,stored_name,created_at); - постановка фоновой проверки (антивирус, генерация превью, извлечение текста);
- публикация файла только после прохождения проверки.
Связанные материалы
- Работа с данными со страницы — формы и
$_FILES - От формы до БД — валидация текстовых полей
- Фильтрация ввода —
filter_varдля строк (файлы — отдельно) - Laravel — структура и валидация
- Первая программа на Laravel
- Работа с базами данных из PHP
Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.
См. также
Другие статьи этого же раздела в боковом меню (как на странице "О разделе"). PHP как язык веб-разработки - роль серверного исполнения, базовый синтаксис и место в современном стеке. Экосистема PHP-приложений - фреймворки, инструменты, пакеты и типовые архитектурные подходы веб-разработки. PHP — это язык программирования общего назначения, который изначально создавался для динамической генерации веб-страниц. Модель исполнения PHP — жизненный цикл запроса, серверный контекст и экосистема популярных CMS. История PHP - эволюция языка от простых скриптов до зрелой платформы серверной веб-разработки. Composer и composer.json — require, install и update, ограничения версий, команды CLI и пример с Laravel dev-веткой. Параметр opcache.fast_shutdown ускоряет завершение работы скрипта за счёт пропуска стандартной процедуры освобождения памяти. Вместо этого используется механизм сборщика мусора операционной системы. Локальная среда разработки на PHP - настройка стека, запуск приложения и отладка без продакшен-сервера. Набор советов, правил, принципов и обычаев в разработке на этом языке. Фреймворки и библиотеки PHP — организация сервисов, вспомогательные компоненты и ускорение разработки веб-приложений. Гайд по установке и настройке с написанием первой программы и её запуском. Примеры простых и полезных консольных приложений с демонстрацией концепций языка.PHP - язык веб-разработки
Экосистема PHP-приложений
Что требуется знать перед началом изучения языка программирования PHP
Модель исполнения PHP
История языка PHP
Composer - управление зависимостями в PHP
Настройка веб-сервера для работы с PHP
Локальная среда разработки на PHP
Рекомендации по разработке на PHP
Фреймворки и библиотеки PHP
Первая программа на PHP
Простые приложения на PHP