Где хранится информация о файлах которые передаются на сервер php
Чтобы загрузить файл на сервер, нам надо использовать форму с параметром enctype=»multipart/form-data» и массив $_FILES . Итак, создадим файл upload.php со следующим содержимым:
METANIT.COM ?>Загрузка файла
Здесь определена форм с атрибутом enctype=»multipart/form-data» . Форма содержит специальное поле для выбора файла.
Все загружаемые файлы попадают в ассоциативный массив $_FILES . Чтобы определить, а есть ли вообще загруженные файлы, можно использовать конструкцию if: if ($_FILES)
Массив $_FILES является двухмерным. Мы можем загрузить набор файлов, и каждый загруженный файл можно получить по ключу, который совпадает со значением атрибута name .
Так как элемент для загрузки файла на форме имеет name=»filename» , то данный файл мы можем получить с помощью выражения $_FILES[«filename»] .
У каждого объекта файла есть свои параметры, которые мы можем получить:
- $_FILES[«file»][«name»] : имя файла
- $_FILES[«file»][«type»] : тип содержимого файла, например, image/jpeg
- $_FILES[«file»][«size»] : размер файла в байтах
- $_FILES[«file»][«tmp_name»] : имя временного файла, сохраненного на сервере
- $_FILES[«file»][«error»] : код ошибки при загрузке
Также мы можем проверить наличие ошибок при загрузке. Если у нас нет ошибки, то поле $_FILES[«filename»][«error»] содержит значение UPLOAD_ERR_OK .
При отправке файла на сервер он сначала загружается во временное место, из которого затем с помощью функции move_uploaded_file() он перемещается в каталог сервера, где расположен скрипт «upload.php».
Также мы можем указать другой путь, например, допустим, на сервере есть папка «upload», тогда, чтобы загружать в нее файлы, необходимо указать соответствующий путь:
if ($_FILES && $_FILES["filename"]["error"]== UPLOAD_ERR_OK) < $name = "upload/" . $_FILES["filename"]["name"]; move_uploaded_file($_FILES["filename"]["tmp_name"], $name); echo "Файл загружен"; >
Функция move_uploaded_file() принимает два параметра путь к загруженному временному файлу и путь, куда надо поместить загруженный файл.
Ограничения и настройка загрузки
По умолчанию размер загружаемых файлов ограничен 2 мб. Однако можно настроить данный показатель в файле конфигурации. Изменим этот показатель, например, до 10 мб. Для этого найдем в файле php.ini следующую строку:
upload_max_filesize = 2M
upload_max_filesize = 10M
Также мы можем настроить папку для временных загружаемых файлов. Для этого в файле php.ini найдем следующую строку:
;upload_tmp_dir =
upload_tmp_dir = "C:/php/upload"
Также в каталоге php нам надо создать папку upload .
Мультизагрузка
Изменим скрипт upload.php так, чтобы он поддерживал множественную загрузку:
METANIT.COM $error) < if ($error == UPLOAD_ERR_OK) < $tmp_name = $_FILES["uploads"]["tmp_name"][$key]; $name = $_FILES["uploads"]["name"][$key]; move_uploaded_file($tmp_name, "$name"); >> echo "Файлы загружены"; > ?>Загрузка файла
Каждое поле выбора файла имеет атрибут name=»uploads[]» , поэтому сервер будет рассматривать набор отправленных файлов как единый массив.
Затем используя цикл foreach , проходим по все файлам и сохраняем их в каталог веб-сайта.
Загрузка файлов на сервер
В прошлом уроке мы научились работать с файлами в PHP, а именно – читать и писать данные в файлы. Теперь давайте рассмотрим возможность языка PHP, позволяющую загружать пользователю файлы на сервер.
Для начала давайте обговорим, как будет выглядеть наше мини-приложение. Пусть у нас будет форма для загрузки файлов: upload.php. Этот же файл будет содержать логику по обработке загружаемых пользователем файлов. И ещё давайте создадим папку uploads, в которую будем складывать все загружаемые файлы.
Давайте для начала напишем саму форму для загрузки файла. Она ничем не отличается от обычной формы для POST-запроса. Единственное отличие – появляется input со специальным типом file. У него также как и у текстового input’а есть атрибут name. Простейшая форма для загрузки файла будет выглядеть следующим образом. Содержимое файла upload.php:
Загрузка файла
Для того, чтобы начать работать с файлом на стороне PHP, нужно познакомиться с ещё одним суперглобальным массивом в PHP — $_FILES. Так как мы указали в input атрибуту name значение attachment, то и узнать информацию о загружаемом нами файле можно получить по соответствующему ключу — $_FILES[‘attachment’].
Давайте разместим PHP-код для обработки загружаемого файла в этом же файле. Для начала просто посмотрим что у нас там вообще есть с помощью var_dump.
?> Загрузка файла

Откроем в браузере нашу форму http://myproject.loc/upload.php, и выберем какой-нибудь файл для загрузки. Я для этого выбрал картинку, валяющуюся на рабочем столе.

После нажатия на кнопку «Отправить» мы получим содержимое массива $_FILES.
FILES» />FILES» />
- PHP программист От 150000₽
- PHP/Golang developer До 2500$
- Стажер \ Джун PHP До 20000₽
- Senior / Middle PHP Developer | Laravel 180000₽ — 250000₽
- Разработчик PHP (middle+) 110000₽ — 130000₽
Значение $_FILES[‘attachment’] представляет собой массив с пятью ключами. Давайте рассмотрим каждый из элементов этого массива более подробно.
- name – имя файла, переданное из браузера;
- type – тип файла, в моём случае это image/png;
- tmp_name – путь до временного файла, который загрузился на сервер, но если с ним до конца запроса ничего не сделать, то он удалится;
- error – код ошибки при загрузке файла, если 0 – то ошибки нет. Изучите возможные коды ошибок вот тут;
- size – размер загруженного файла в байтах.
Как мы уже сказали, пока что на сервере создан временный файл. Чтобы его сохранить нужно вызвать специальную функцию, которая переместит его из временной папки в нужную нам папку (в нашем случае это папка uploads). Эта функция — move_uploaded_file(). Первым аргументом она принимает путь до временного файла (в нашем случае — $_FILES[‘attachment’][‘tmp_name’]), а вторым аргументом – путь, по которому нужно его сохранить. Функция вернёт true, если всё ок, и false, если что-то пошло не так.
ВНИМАНИЕ! Для перемещения временного файла в нужную вам папку стоит всегда использовать функцию move_uploaded_file(). Она предварительно проверяет, что указанный файл действительно является временным загруженным файлом, а не чем-то ещё. Другие функции для копирования файлов не подойдут, так как они эту проверку не выполняют, а это небезопасно. Кому интересно – можете подробнее прочитать тут.
В простейшем виде скрипт для загрузки файла на сервер будет выглядеть следующим образом.
else < $result = 'http://myproject.loc/uploads/' . $srcFileName; >> ?> Загрузка файла

Если сейчас выбрать файл и нажать на кнопку “Отправить”, то скрипт вернёт нам адрес загруженного файла.
Мы можем открыть этот адрес в новой вкладке, и убедиться, что действительно файл доступен по этому адресу. Ну и если посмотрим в папку uploads, то тоже увидим, что наш файлик теперь там валяется.
Этот скрипт ещё очень сырой, давайте его доработаем.
Проверка на существующий файл с таким же именем
Первое, что приходит на ум – убедиться, что файл с таким именем не загружался ранее. Иначе мы рискуем перезатереть что-нибудь важное. Давайте проверим существование такого файла с помощью функции file_exists().
elseif (!move_uploaded_file($file['tmp_name'], $newFilePath)) < $error = 'Ошибка при загрузке файла'; >else < $result = 'http://myproject.loc/uploads/' . $srcFileName; >> ?> .
Обработка ошибок при загрузке файлов
Теперь вспомним об элементе массива по ключу error. Как мы помним, при отсутствии ошибок там будет 0. Во всех остальных случаях стоит уведомить пользователя об ошибках при загрузке файла. Если мы прочитаем вот эту страничку документации, то увидим, что ошибки на самом деле могут быть самыми разными.
Хороший код должен обрабатывать каждую из этих ошибок и выдавать соответствующую информацию пользователю. Мы же в учебном примере ограничимся только проверкой на 0, будет большим плюсом, если вы самостоятельно напишите обработку всех этих ошибок в своём коде. Итак, результат.
elseif (file_exists($newFilePath)) < $error = 'Файл с таким именем уже существует'; >elseif (!move_uploaded_file($file['tmp_name'], $newFilePath)) < $error = 'Ошибка при загрузке файла'; >else < $result = 'http://myproject.loc/uploads/' . $srcFileName; >> ?> .
Проверяем расширение загружаемого файла
Ещё необходимо проверить то, что у загружаемого файла корректное расширение. Например, если мы хотим чтобы через форму загружали только картинки, то допустимыми расширениями для нас будут .jpg, .gif, .png.
Если этого не предусмотреть – то через форму загрузки можно будет загрузить какой-нибудь вредоносный скрипт. Представьте, что кто-то загрузил на сервер .php-скрипт, который удаляет все файлы в текущей директории, а затем просто запустил его, зайдя по адресу http://myproject.loc/uploads/very_bad_script.php. И это еще не самое страшное, что могут сделать злобные хакеры. Поэтому нужно всегда контролировать то, что пользователи загружают на сервер.
Получить расширение файла можно при помощи функции pathinfo(). Первым аргументом передаётся путь до файла, вторым – константа PATHINFO_EXTENSION.
$extension = pathinfo($srcFileName, PATHINFO_EXTENSION);
В переменной $extension после это будет строка c расширением загруженного файла, например, png.
Нам остаётся только проверить, что расширение загружаемого файла находится в списке разрешенных. Давайте создадим массив со списком таких расширений и проверим наличие расширения загружаемого файла в списке разрешенных.
$allowedExtensions = ['jpg', 'png', 'gif']; $extension = pathinfo($srcFileName, PATHINFO_EXTENSION); if (!in_array($extension, $allowedExtensions))

Теперь, если вы попробуете загрузить файл с каким-то другим расширением, то скрипт на нас ругнётся.
Результат
В ходе данного урока получили скрипт следующего содержания.
elseif ($file['error'] !== UPLOAD_ERR_OK) < $error = 'Ошибка при загрузке файла.'; >elseif (file_exists($newFilePath)) < $error = 'Файл с таким именем уже существует'; >elseif (!move_uploaded_file($file['tmp_name'], $newFilePath)) < $error = 'Ошибка при загрузке файла'; >else < $result = 'http://myproject.loc/uploads/' . $srcFileName; >> ?> Загрузка файла
Есть ещё несколько проверок, которые было бы неплохо провести, прежде чем позволить загрузить файл на сервер. О них вы узнаете в домашнем задании.
PHP: загрузка файла на сервер


Загрузка файлов через PHP — весьма интересное дело, к которому нужно подходить очень внимательно. В интернете можно найти множество примеров реализации загрузки файлов, но не все они хороши и отвечают правилам безопасности.
Подобные вещи нужно доводить до конца, даже если на это уйдет много времени. Если вы оставите брешь в коде, то весь ваш сервер может оказаться под угрозой.
Безопасность
При помощи PHP загрузка файлов на сервер осуществляется достаточно легко. Код очень короткий и простой. Всего лишь пара строк. Но такой метод опасен. Куда больше времени и строк кода уходит на безопасность.
Опасность заключается в том, что, если не делать проверок, любой злоумышленник сможет загрузить свои скрипты на ваш сервер. При этом у него будет полный доступ. Он сможет сделать всё, что захочет:
- удалить базы;
- удалить файлы сайтов;
- изменить файлы сайтов;
- добавить свою рекламу на ваш сайт;
- загрузить вирусы;
- перенаправлять всех пользователей на свои сайты;
- и многое другое, что придет в голову взломщику.
Всегда нужно проверять, что за файл пытается загрузить пользователь. Если, например, вы загружаете только фотографии, необходимо проверить, что этот файл точно является изображением. В противном случае к вам загрузят что угодно.
При загрузке файлов на сайт и при работе веб-ресурса возникает задача удаления устаревших или.
Как именно реализовать проверку, будет показано далее, при непосредственном рассмотрении скрипта загрузки файлов.
Создание формы PHP
Форма загрузки файла выглядит очень просто. Хватает кнопки обзора и кнопки загрузки.

Описывать создание формы не будем, так как это легко. Дальнейшие инструкции предполагают, что у вас уже есть базовые понятия HTML (иначе бы вы не искали информацию о загрузке на PHP).
Но обратите внимание, что для передачи данных в форме нужно добавить атрибут enctype.

Иначе данные о файле обработчику передаваться не будут.
Как это должно работать?

При нажатии на кнопку обзора у вас должно открыться окошко, где предложат выбрать файл.
После этого должен будет появиться путь, где расположен файл.

Если путь не появится, то проделайте это действие еще раз.
После нажатия на кнопку загрузки файлом-обработчиком можно выдать любую информацию.

Например, можно написать строку, в которой говорится, что файл с «таким-то» названием был успешно загружен в «такую-то» папку. Разумеется, имя файла будет выдаваться всегда разное.
Обычно такую подробную информацию используют для отладки кода. Таким образом можно проверить, что данные передаются и запись происходит в нужную вам директорию. То есть даже имя файла не указывают. Поскольку это лишняя информация, которая пользователю не нужна.
Оптимизация установки и настройки PHP — перспективное занятие. Это.
Имеет смысл вывод данных об имени только в том случае, если пользователь загружает несколько файлов. Такой случай рассмотрим немного дальше. Не будем забегать вперед.
Настройка
В PHP загрузка файла на сервер требует определенных настроек, которые нужно делать в файле php.ini. В этом файле очень много настроек. Они все нам не нужны. Нас интересуют три строчки: file_uploads, upload_tmp_dir и upload_max_filesize.
Обратите внимание на то, что эти настройки коснутся всех ваших сайтов на сервере, а не только какого-то одного. Поэтому задавайте максимальный размер исходя из того, что у вас будут загружать пользователи. Не рекомендуется задавать слишком большие значения.
После того, как вы изменили значения в этих параметрах, сервер нужно перезагружать. Иначе настройки не вступят в силу, так как они считываются в момент загрузки сервера.
Сделать это можно в консоли, подключившись через SSH к серверу. Достаточно ввести команду service httpd restart, и после этого настройки вступят в силу.
Другой способ — перезапуск через ISP-панель или через биллинг-панель провайдера.

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

Выберите любой файл и нажмите «Загрузить». На странице обработчика выведется информация, которая хранится в $_FILES. Переменная пишется полностью с большими буквами. PHP — язык регистрозависимый.

Как видите, в этом массиве есть много полей. Все они для нас важны. В первом поле хранится название файла в том виде, в котором оно используется на вашем компьютере.
В графе type указан тип файла. Полю tmp_name соответствует имя временного файла. После окончания работы скрипта он будет удален.
В поле error хранится код ошибки. Об этом немного далее. Size — размер в байтах.
Ошибки
Осуществляемая через PHP загрузка файла всегда сопровождается кодом ошибки. Сообщение об ошибке заключено в поле «error». На скриншоте ошибка равна нулю.

Рассмотрим значения всех ошибок:

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

PHP: скрипт загрузки файла
Как же все осуществляется на практике? В PHP загрузка файла происходит командой copy. Если вас интересовал вопрос о том, как загрузить файл, то ответом будет просто copy, в которой используется два параметра — исходный файл и файл назначения.
Чтение содержимого файла целиком — естественная операция, и во многих.
Но, как писалось выше, ограничиваться этим нельзя в целях безопасности. Например, проверить что за файл мы грузим, можно при помощи поля type в массиве $_FILES. Сначала разберемся с проверкой, а потом перейдем к полноценному скрипту
Допустим, вы хотите, чтобы пользователи могли загрузить фотографию с разрешением только GIF, JPEG или PNG. Указать это можно вот так.
if($_FILES[‘file_upload’][‘type’] != «image/gif») echo «Извините, мы поддерживаем загрузку только Gif-файлов»;
exit;
>
Если хотите грузить все 3 типа, то просто добавьте дополнительное условие с другим типом изображения.
Копирование делается вот так: copy(файл 1, файл 2).
В нашем случае, когда работа идет с загрузкой с компьютера на сервер, можно делать так
То есть файл будет копироваться с названием 1.jpg. Это не совсем правильно. В данном случае это только пример. Имя файла всегда нужно задавать разное, и расширение указывать в зависимости от файла.
Определить расширение можно разными способами. Всё зависит от эрудиции разработчика. Один из самых быстрых способов (разница в десятых долях секундах) определения расширения — это следующий код.
В переменной $ext у нас будет храниться искомое расширение. А имя файла можно задать случайным образом при помощи md5. Если вы планируете загружать много файлов, то лучше грузить их в разные папки. Так будет удобнее. В особенности если захотите провести чистку.
Код для загрузки будет следующим.

if ($_FILES[‘photo1’][‘tmp_name’] == null)
/// . Допустим, у вас для какого-нибудь проекта на сервере разрешено грузить большие файлы (видео), но здесь будут только фотографии, и пользователей нужно ограничить
if (($_FILES[«photo1»][«size»] > 102 102 2)
Максимально разрешённый размер изображения 2 Мб
// создание папки текущего месяца
// создание папки текущего дня
/// генерируем имя файла
echo («файл успешно загружен»);
/// любые дальнейшие действия (запись в базу и т. п.)
Несколько файлов
Загрузка нескольких файлов (PHP) происходит при помощи дополнительных полей в форме.

Данный способ не очень хороший, так как ограничивает количество файлов для загрузки. Более того, считается плохим тоном в программировании. Старайтесь делать всё динамичное.
Идеальный вариант — это возможность выбора сразу большого числа файлов нажатием на одну кнопку.
Для этого форму создаем вот таким кодом.
Обратите внимание, что добавлено слово multiple, и имя задано как массив []. В этом случае массив $_FILES будет немного отличаться. У вас получится массив в массиве.
Для проверки можете опять воспользоваться var_dump($_FILES);
Все ваши файлы будут располагаться в массиве вот так:
- $_FILES[«file1»][«name»][0]
- $_FILES[«file1»][«name»][1]
- И так далее.
В скобках пишется номер файла в массиве. Отсчет с нуля. Обрабатываем их точно так же, просто задаем цикл и при обращении в выше описанном коде в конце добавляем индекс [$i].
/// вставляем вышеописанный код
Таким образом, у вас будет происходить через PHP загрузка файлов на сервер в одном цикле без лишнего повторения кода, как это обычно бывает, если использовать вариант со статичным количеством файлов (последнее фото).

При загрузке файлов на сайт и при работе веб-ресурса возникает задача удаления устаревших или ненужных файлов. Для решения этой задачи PHP предлагает функцию unlink. Для безопасного удаления важное значение имеют имя файла и организация процесса.

Оптимизация установки и настройки PHP — перспективное занятие. Это делается быстро, особенных затрат не несет, но ориентируясь на использование PHP как основы системы объектов, созданной для решения задачи, трудно достичь желаемого результата.

Чтение содержимого файла целиком — естественная операция, и во многих случаях лучшее решение — использование функции file_get_contents на PHP.

Достаточно часто владельцы хостингов мучаются из-за постоянного возникновения сообщения об ошибке 500 (Internal Server Error). Она, как это называется, многим попросту отравляет жизнь. Сейчас мы попробуем кратко разобраться в сути самой ситуации и .

ЧПУ-ссылки: что это такое и как реализовать. Несколько различных способов настройки в популярных системах управления контентом.
Безопасная загрузка изображений на сервер. Часть вторая
Итак, после применения описанных в первой части методов, мы можем прекратить волноваться? К сожалению, нет. То, какие расширения файла будут переданы транслятору PHP, будет зависеть от конфигурации сервера. Разработчик часто не знает и не контролирует конфигурацию веб-сервера. Мы видели веб-серверы, с такой конфигурацией, что файлы .html и .js выполнялись как php. Некоторые веб-приложения могут потребовать, чтобы файлы .gif или .jpeg интерпретировались PHP (это часто случается, когда изображения, например графы и диаграммы, динамически строятся на сервере самим PHP).
Даже если мы знаем точно, какие расширения файла интерпретируются PHP, у нас нет никакой гарантии, что это не изменится в будущем, когда другие приложения будут установлены на сервер. К тому времени можно забыть, что безопасность нашего сервера зависит от этих изменений.
Если у вас веб-сервер на основе Microsoft IIS, то надо иметь в виду еще несколько моментов. В отличие от Apache, сервер Microsoft IIS поддерживает выполнение «PUT»-запросов, которые позволят пользователям загружать файлы непосредственно, минуя PHP. PUT-запросы могут быть использованы для загрузки файлов на сервер, если системные права позволяют это сделать IIS (он запущен как IUSR_MACHINENAME). Это можно настроить с помощью Services Manager:

Чтобы позволить PHP загружать файлы, Вы должны изменить разрешения файловой системы сделать директорий доступным для записи. Очень важно удостовериться, что разрешения IIS не позволяют записывать файлы. Иначе пользователи будут в состоянии загрузить произвольные файлы с помощью PUT-запросов, обходя любые проверки, которые вы сделаете в PHP.
Косвенный доступ к загруженным файлам
Иногда невозможно дать прямой доступ к директории с загрузкой. Это может быть вызвано тем, что данная директория не находится под корнем сайта или доступ ней ограничен с помощью настроек сервера или .htaccess.
Рассмотрим следующий пример (upload5.php):
$uploaddir = ‘/var/spool/uploads/’ ; # Outside of web root
$uploadfile = $uploaddir . basename($_FILES[ ‘userfile’ ][ ‘name’ ]);
if (move_uploaded_file($_FILES[ ‘userfile’ ][ ‘tmp_name’ ], $uploadfile)) echo «File is valid, and was successfully uploaded.\n» ;
> else echo «File uploading failed.\n» ;
>
?>
* This source code was highlighted with Source Code Highlighter .
Пользователи не могут просто обратиться к /uploads/, чтобы загрузить файлы, и мы должны обеспечить дополнительную возможность этого (view5.php):
* This source code was highlighted with Source Code Highlighter .
Код view5.php обладает серьезной уязвимостью. Злоумышленник может использовать этот код, чтобы прочитать любой файл, который может быть прочитан на уровне прав веб-сервера. Например, если вызвать
www.example.com/view5.php?name=../../../etc/passwd, то возможно получится прочитать файл с паролями.
Этот баг может быть пофиксен с помощью функции результата dirname(realpath()), которая возвращает реальный путь к файлу. Таким образом, если этот путь некорректен, то не показываем файл. Аналогично с basename() – получаем только имя файла, которое уже присоединяем к корректному директорию загрузки. – Прим. переводчика.
Использование локальных include (Local file inclusion attacks)
Это одна из самых страшных дыр безопасности на сайтах. Она настолько хорошо известна, что в действительности уже почти не проявляется. Но, как говорится – «повторение – мать учения». – Прим. переводчика.
Прошлый пример хранит загруженные файлы за пределами корня, где к ним нельзя получить прямой доступ и выполнить. Хотя это и безопасно, но у злоумышленника может быть шанс использовать это в своих интересах, если в коде присутствует другая уязвимость – использование include. Предположим, что у нас есть некоторая другая страница, которая содержит следующий код (local_include.php):
if (isset($_COOKIE[ ‘lang’ ])) $lang = $_COOKIE[ ‘lang’ ];
> elseif (isset($_GET[ ‘lang’ ])) $lang = $_GET[ ‘lang’ ];
> else $lang = ‘english’ ;
>
// . some more code here
?>
* This source code was highlighted with Source Code Highlighter .
Это общая часть кода, которая обычно имеет место в многоязычных веб-приложениях. Подобный код может обеспечить различный include файлов в зависимости от пользовательских предпочтений.
Код имеет от include-уязвимость. Нападавший может заставить эту страницу включать любой файл из файловой системы, например:

Этот запрос заставляет local_include.php включать и выполнить «language/../../../../../../../../tmp/phpinfo», который является просто/tmp/phpinfo. Нападавший может выполнить только файлы, которые уже находятся на стороне сервера, таким образом его возможности ограничены.
Но, если нападавший в состоянии загрузить файлы, даже вне корня сайта, и он знает название и местоположение загруженного файла, с помощью подобной уязвимости он может выполнить произвольный код на сервере.
Раньше существовала настройка php, которая позволяла инклудить файлы по url, таким образом можно было выполнить код, который мог и не находиться на сервере. В последних версиях php такой возможности в принципе нет из соображений безопасности. – Прим. переводчика
В итоге
При обеспечении защиты важно препятствовать тому, чтобы злоумышленник знал название загруженного файла. Это может быть сделано случайной генерацией имён загружаемых файлов, с сохранением оригинальных в БД.
Рассмотрим пример (upload6.php):
require_once ‘DB.php’ ; // We are using PEAR::DB module
$uploaddir = ‘/var/spool/uploads/’ ; // Outside of web root
$uploadfile = tempnam($uploaddir, «upload_» );
if (move_uploaded_file($_FILES[ ‘userfile’ ][ ‘tmp_name’ ], $uploadfile)) // Saving information about this file in the DB
$db =& DB::connect( «mysql://username:password@localhost/database» );
if (PEAR::isError($db)) unlink($uploadfile);
die «Error connecting to the database» ;
>
$res = $db->query( «INSERT INTO uploads SET name=?, original_name=?, mime_type=?» ,
array(basename($uploadfile,
basename($_FILES[ ‘userfile’ ][ ‘name’ ]),
$_FILES[ ‘userfile’ ][ ‘type’ ]));
if (PEAR::isError($res)) unlink($uploadfile);
die «Error saving data to the database. The file was not uploaded» ;
>
$id = $db->getOne( ‘SELECT LAST_INSERT_ID() FROM uploads’ ); // MySQL specific
Просмотр загруженного файла (view6.php):
if (!is_numeric($id)) die( «File id must be numeric» );
>
if (PEAR::isError($db)) die( «Error connecting to the database» );
>
$file = $db->getRow( ‘SELECT name, mime_type FROM uploads WHERE >,
array($id), DB_FETCHMODE_ASSOC);
if (PEAR::isError($file)) die( «Error fetching data from the database» );
>
if (is_null($file) || count($file)==0) die( «File not found» );
>
header( «Content-Type: » . $file[ ‘mime_type’ ]);
readfile($uploaddir.$file[ ‘name’ ]);
?>
* This source code was highlighted with Source Code Highlighter .
Теперь загруженные файлы нельзя непосредственно выполнить (потому что они сохранены за пределами корня). Они не могут использоваться в include-уязвимостях, потому что у нападавшего нет возможности узнать имя загруженного файла в файловой системе на сервере. Есть некоторая проблема с «пересечением» файлов, потому что файлы привязаны к числовому индексу, а не к его имени. Также хотелось бы указать на использование PEAR::DB для SQL-запросов. В нашем SQL используются вопросительные знаки как места для переменных запроса. Когда данные, полученные от пользователя, передаются в запрос, их типы автоматически расставляются, предотвращая проблемы SQL-инъекций.
Альтернативой тому, чтобы хранить файлы в файловой системе является хранение файлов непосредственно в базе данных с помощью полей BLOB. У этого подхода есть тоже свои плюсы, но в большинстве случаев он не применяется, из-за своей огромной ресурсоемкости.
Другие проблемы
- Отказ в обслуживании. Пользователь может загрузить несколько больших файлов, тем самым заняв все свободное место на сервере. Это решается, выставлением ограничения на размер загружаемого файла и на количество загружаемых файлов от одного пользователя в день.
- Производительность. В последнем примере при частых запросах на показ файла просмотр может быть узким местом. Если сервер сильно нагружен, то рекомендуется использовать еще один, предназначенный только для хранения статичного контента, на котором не может быть выполнен php-код. Еще одним способом повысить производительность является использование кэширующего прокси-сервера, который предотвращает повторную обработку статического контента на стороне сервера выдавая соответствующие заголовки.
- Управление доступом. Во всех примерах выше мы предполагали, что любой пользователь может просмотреть любой загруженный файл. Однако, может потребоваться, что бы только тот пользователь который загрузил файл мог просмотреть его. В этом случае при загрузке должна сохраняться информация о владельце файла. При просмотре файла должна быть соответствующая проверка.
Заключение
При разработке загрузки файлов на сервер необходимо учитывать описанные в статье уязвимости, что бы не стать жертвой злоумышленников.
Лучше всего не давать пользователям обращаться напрямую к загружаемым файлам. Это может быть сделано путем хранения загруженных файлов за пределами корня сайта или запрещая доступ к данной директории с помощью конфигурации веб-сервера.
Другой важной мерой безопасности – не хранить файлы на сервере под оригинальными именами файлов. Это предотвратит возможности Include-уязвимостей, даже если они есть, а так же сделает любую манипуляцию с именами файлов для злоумышленника невозможной.
Проверка формата изображения через PHP не дает никакой гарантии, что данный файл не может быть выполнен как php-скрипт. Можно создать корректное изображение, которое в тоже время будет являться выполнимым php-скриптом.
Приходящим от клиента данным, таким как Content-Type и расширение файла вообще доверять нельзя. Их очень просто подделать. Более того, список исполняемых расширений зависит целиком от веб-сервера, и нет никакой гарантии что он со временем не изменится.
Производительность очень важна, но совершенно невозможно обеспечить безопасную загрузку файлов на сервер без ущерба для неё.
