Заметки о загрузке файлов с PHP

Знаете, я не специалист по безопастности, но постоянно встречаюсь с элементарными проблемами любого WEB-программиста. А за последнюю неделю в жизни/работе призошло не мало изменений, связанных с безопасностью. Первое что очень сильно повлияло – это PHP Security conference, на котором показали на сколько бывают глупы создатели сайтов. Второе – это то, что я уже проверить и накопать на окружающих сайтах и своём коде.

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

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

И я не буду скрывать, что я купился на этой форме. Да, тут я лажанулся. Я понадеялся на getimagesize. Оказалось – зря. В первую очередь нужно проверять расширение файла в массиве $_FILES[‘userfile’][‘name’] и ещё перед выполнением move_uploaded_file. И не забудьте провериться перед этим с is_uploaded_file.

Вот на расширении файла я и попался. Я этим (по непонятным мне причинам) очень сильно принебрёг и понадеялся на функцию getimagesize. А вот она то и подвела. Оказывается (установлено экспериментальным путём на локальном PHP версии 4.4) – при загрузке файла с расширением .php (помните, я этим принебрёг), если в этот файл всунуть первые 128 байт из PNG файла, getimagesize считает что это картинка. Соответственно PHP файл попал на сервер, а что дальше бывает, я вам рассказывать не буду.

Некоторые говорят, что нужно ещё проверять $_FILES[‘userfile’][‘type’], но доверять ему нельзя – это данные, которые нам посылает клиент и они могут быть легко потделаны на строне клиента.

Загружая файл, не рекомендуют полностью доверят и $_FILES[‘userfile’][‘name’] – только профильтровав, или только расширение. Если же его прямо вписать, возможна атака путём подделки имени файла (скажем впишут вам ../../../index.php, а если у вас suEXEC или su_php можно и пострадать). Причём, они (создатели PHP) даже не подумали это дело отфильтровать автоматически, хотя в примере Validating file uploads создатели мануала об этом подумали (сам пример на данный момент мне не понятен).

Если разобрать пример Validating file uploads, а мне он виден вот таким совсем не понятно, каким образом они установили что происходит “Possible file upload attack!”? Ведь файл будет сдвинут в каталог /var/www/uploads/, а если он не будет сдвинут – следовательно, либо нету прав на это, либо исходный файл пропал. По моему, там должна была функция is_uploaded_file, а move_uploaded_file уже потом.

Итак, короткое резюме: проверять файл по разрешению и разрешать загружать только те файлы, которые 100% не будут выполнены сервером как скрипты (я бы разрешал загружать только картинки); не доверять $_FILES[‘userfile’][‘name’], проверять и фильтровать её, если используете; если файл переноситься во временное место для обработки, постарайтесь это место либо держать вне DocumentRoot, либо закрыть его при помощи .htaccess; не доверять вообще $_FILES[‘userfile’][‘type’]; пользуйтесь функциями is_uploaded_file и move_uploaded_file; проверяйте и перепроверяйте параметры.

Если у вас есть комментарии на эту тему, советы или замечания какие – пишите.

Leave a Reply

Your email address will not be published.

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.