Популярные скрипты
 
Раздел: Статьи / Безопасность Добавить статью

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

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

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

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

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

<?php

if(isset($_POST['upload'])){

if($_FILES['uploadFile']['type'] != "image/gif") {

echo "Ошибка, Вы можете загружать только gif картинки";

exit;
}

$folder = 'path/to/folder/';

$uploadedFile = $folder.basename($_FILES['uploadFile']['name']);

if(is_uploaded_file($_FILES['uploadFile']['tmp_name'])){

if(move_uploaded_file($_FILES['uploadFile']['tmp_name'], $uploadedFile)){

echo Файл загружен;
}
else {

echo "Во время загрузки файла произошла ошибка";
}
}
else {

echo "Файл не загружен";
}
}

?>

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

Он может написать небольшой Perl-скрипт(возможно на любом языке), который будет эмулировать действия пользователя по загрузке файлов, дабы изменить отправляемые данные на свое усмотрение.Так как проверяемый MIME-тип приходит вместе с запросом, то ничего не мешает злоумышленнику установить его в «image/gif», поскольку с помощью эмуляции клиента он полностью управляет запросом, который посылает.

Если вы загружаете только изображения,то не стоит доверять заголовку Content-Type, а лучше проверить фактическое содержание загруженного файла, чтобы удостовериться что это действительно изображение. Для этого в РНР очень часто используют функцию getimagesize().

Функция getimagesize() определяет размер изображения GIF, JPG, PNG, SWF, PSD, TIFF или BMP и возвращает размеры, тип файла и высоту/ширину текстовой строки, используемой внутри нормального HTML-тэга IMG.

Давайте посмотрим, как можно использовать эту функцию в нашем скрипте:

<?php

if(isset($_POST['upload'])){

$imageinfo = getimagesize($_FILES['uploadFile']['tmp_name']);

if($imageinfo['mime'] != 'image/gif' &&
$imageinfo['mime'] != 'image/jpeg'){

echo "Вы можете загружать только gif и jpeg картинки!";
exit;
}

$folder = 'path/to/folder/';

$uploadedFile = $folder.basename($_FILES['uploadFile']['name']);

if(is_uploaded_file($_FILES['uploadFile']['tmp_name'])){

if(move_uploaded_file($_FILES['uploadFile']['tmp_name'],

$uploadedFile)){

echo Файл загружен;
}
else {

echo "Во время загрузки файла произошла ошибка";
}
}
else {

echo "Файл не загружен";
}
}

?>

Можно подумать, что теперь мы можем пребывать в уверенности, что будут загружаться только файлы GIF или JPEG. К сожалению, это не так. Файл может быть действительно в формате GIF или JPEG, и в то же время PHP-скриптом. Большинство форматов изображения позволяет внести в изображение текстовые метаданные. Возможно создать совершенно корректное изображение, которое содержит некоторый код PHP в этих метаданных. Когда getimagesize() смотрит на файл, он воспримет это как корректный GIF или JPEG. Когда транслятор PHP смотрит на файл, он видит выполнимый код PHP в некотором двоичном «мусоре», который будет игнорирован.

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

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

<?php
if(isset($_POST['upload'])){

$whitelist = array(".gif", ".jpeg", ".png");

$error = true;

//Проверяем разрешение файла
foreach ($whitelist as $item) {
if(preg_match("/$item$/i",$_FILES['userfile']['name'])) $error = false;
}

if($error) die("Ошибка, Вы можете загружать только gif,jpeg,png картинки");

$folder = 'path/to/folder/';

$uploadedFile = $folder.basename($_FILES['uploadFile']['name']);

if(is_uploaded_file($_FILES['uploadFile']['tmp_name'])){

if(move_uploaded_file($_FILES['uploadFile']['tmp_name'],

$uploadedFile)){

echo Файл загружен;
}
else {

echo "Во время загрузки файла произошла ошибка";
}
}
else {

echo "Файл не загружен";
}
}

?>


Выражение !preg_match ("/$item$/i", $_FILES['uploadFile']['name']) проверяет соответствие имени файла, определенному пользователем в массиве белого списка. Модификатор «i» говорит, что наше выражение регистронезависимое. Если расширение файла соответствует одному из пунктов в белом списке, файл будет загружен, иначе скрипт выдаст ошибку!

Как видите доверять расширению и Content-Type файла нельзя. Поэтому необходимо в каталог, куда закачиваются все Ваши файлы добавить файл .htaccess куда прописать следующие строки:

RemoveHandler .php .php5 .php4 .php3 .phtml .pl
AddType text/plain .php .php .htm .html .phtml .pl

Данные строки в .htaccess заставит сервер не исполнять php и др. файлы, а выводить их содержимое на экран.

На этом все! Удачи!

Автор: Шамшур Иван
сайт автора: Начинающему веб-мастеру - от простого к сложному
Добавлено 2011.05.23


Раздел: Статьи / Безопасность

 
Популярные статьи