Выполнение кода с правами apache — это, конечно, не полный контроль над сервером, но не стоит недооценивать открывающиеся злоумышленнику возможности: он получает полный доступ ко всем скриптам и конфигурационным файлам сайта и через них — к используемым БД; он может рассылать от вашего имени спам, захостить у вас какой-нибудь незаконный контент, тем подставив вас под абузы; может, найдя параметры привязки к платёжной системе, отрефандить все заказы и оставить вас без дохода за весь последний месяц. Обидно, правда?
Как ему это удастся?
Вы не проверяете загружаемые файлы вообще?
Клинический случай: вы скопировали из мануала по PHP строчку
Код: Выделить всё
move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]);
Так я могу заливать вообще любые файлы, причём не только в каталог upload, а куда угодно — могу указать в качестве имени файла ../index.html и заменить вашу главную страницу своей. Обидно, правда?
Добавьте хотя бы вызов basename() для $_FILES["file"]["name"]. В последней версии PHP, судя по моим экспериментам, $_FILES["file"]["name"] и так возвращает только basename от переданного в HTTP-запросе значения filename; но осторожность тут лишней не будет.
Вы проверяете, является ли файл картинкой?
Перед тем, как сохранить загруженный файл в upload/, вы вызываете getimagesize(), чтоб убедиться, что загружена именно картинка. К превеликому удовольствию «исследователей безопасности», PHP позволяет вставлять выполнимый код в любое место любого файла, игнорируя всё, что не заключено в теги <? ?>. Я могу взять фотографию своего любимого котика, дописать в её конец что-нибудь навроде
Код: Выделить всё
<?php passthru($_GET['c']); ?>
Вы проверяете mime-type?
Почему нельзя доверять полю «mime», возвращаемому getimagesize(), понятно: оно берётся на основании заголовка файла. Тем более нельзя доверять $_FILES["file"]["type"] — ничто не мешает мне передать в HTTP-запросе «Content-Type: image/jpeg» перед PHP-скриптом. Это кажется банальным, но люди действительно на это полагаются. Вот я только что видел проверку
Код: Выделить всё
if(substr($_FILES["file"]["type"], 0, 6)=="image/") {/* сохранить файл */}
Вы проверяете расширение?
В другом самоуверенном проекте я только что видел проверку
Код: Выделить всё
if(substr($_FILES["file"]["name"], -3)=="jpg" || substr($_FILES["file"]["name"], -3)=="gif" || substr($_FILES["file"]["name"], -3)=="png") {/* сохранить файл */}
Вы проверяете «нехорошие» расширения?
В третьем самоуверенном проекте я видел проверку
Код: Выделить всё
if(strpos($_FILES["file"]["name"], ".php")===FALSE) {/* сохранить файл */}
Вы проверяете расширение правильно?
Предположим, вы убедились, что имя файла заканчивается на .jpg (вместе с точкой!) Можно ли быть уверенным, что PHP-код в нём не будет выполняться? Учитывая предыдущий пункт — скорее всего можно, но гарантии нет. Может случиться, что злоумышленник (или криворукий админ) как-то повредил /etc/mime.types, так что расширение .jpg окажется незарегистрированным, и Apache станет анализировать расширение перед ним. Может случиться, что /etc/mime.types стал недоступен из-за chroot. Может случиться, что злоумышленник (или криворукий админ) забросил в каталог upload/ такой .htaccess, который позволяет выполнять PHP-код во всех файлах подряд.
Кроме того, до недавних версий PHP можно было загрузить файл с именем «pwn.php\0.jpg», который при сохранении на диск превращался бы в «pwn.php», потому что для системных вызовов строка с именем файла заканчивается на \0, а для функций PHP это обычный допустимый в строке символ. В таком случае проверка расширения тоже ничего не гарантировала бы.
И что же делать?
Исчерпывающий чеклист попытались составить на stackoverflow. Если коротко:
а) запретите явно выполнение PHP в каталоге, куда сохраняются пользовательские файлы;
б) переименовывайте пользовательские файлы в неподконтрольные пользователям имена;
в) если возможно, пересохраняйте картинки при помощи GD, чтобы удалить ненужные метаданные и непрошенные приписки в конце файла.
Источник: http://habrahabr.ru/post/148999/