Публикация изображений и файлов

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

Загрузка файла на сервер (427.php)

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Загрузка файла на сервер (427.php)</title>
</head>

<body>
<form action="" method="post" enctype="multipart/form-data">
<input name="filename" type="file" size="35">
<input name="" type="button" value="Загрузить">
</form>
<?php
//Обработчик формы
if(!empty($_FILES['filename']['tmp_name']))
{
//Сохраняем файл в текущем каталоге
if(copy($_FILES['filename']['tmp_name'], $_FILES['filename']['name']))
{
echo "Файл успешно загружен - <a href=".$_FILES['filename']['name'].">".$_FILES['filename']['name']."</a>";
}
}
?>
</body>
</html>

Уязвимость скрипта, представленного в данном примере, заключается в том, что он позволяет загрузит любой файл, в том числе и РНР-скрипт. При помощи данного загрузчика можно загрузить на сервер скрипт для редактирования файлов (см. здесь), назвав его предварительно, например, 428.php. Он позволит открыть файл 427.php и добавить в его конец код удаляющий файлы 427.php и 428.php.

Редактирование файла на сервере

Теперь остается загрузить файл 427.php и перезагрузить его: ни самого файла, ни 428.php, по которому можно было догадаться о взломе, на сервере не останется.

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

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

Защита скрипта (428.php)

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Защита скрипта</title>
</head>

<body>
<form action="" method="post" enctype="multipart/form-data">
<input name="filename" type="file" size="30">
<input name="" type="submit" value="Отправить">
</form>
<?php
//Обработчик формы
if(!empty($_FILES['filename']['tmp_name']))
{
//Извлекаем из имени файла расширение

$ext=strtolower(strrchr($_FILES['filename']['name'], "."));
//разрешаем загружать файлы только определенного формата
$extentions=array(".jpg", ".gif");
//проверяем, входит ли расширение файла
//в список зарегистрированных

if(in_array($ext, $extentions))
{
//сохраняем файл в текущем каталоге
if(copy($_FILES['filename']['tmp_name'], $_FILES['filename']['name']))
{
echo "Файл успешно загружен - <a href=".$_FILES['filename']['name'].">".$_FILES['filename']['name']."</a>";
}
}
else
{
//файл с незарегистрированным расширением
echo "Разрешена загрузка только изображений";
}
}
?>
</body>
</html>

В скрипте, сразу после загрузки файла на сервер извлекается его расширение, которое помещается в пременную $ext. Массив $extentions содержит список расширений файлов, разрешенных для загрузки на сервер. Если расширение только что загруженного файла совпадает с одним из расширений, зафиксированных в массиве $extentions, файл копируется из временного каталога в каталог назначения. Если расширение загруженного файла не совпадает ни с одним из зарегистрированных расширений, то файл игнорируется.

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

Еще одним способом проверки файлов является проверка типа файла, который можно узнать, обратившись к элементу суперглобального массива $_FILES['filename']['type']. В случае графических файлов данный элемент получает значения 'image/gif', 'image/pjpeg' и т.д. В то же время РНР- или HTML-файл будут иметь тип 'text/plain'. Если небходимо загружать только графические файлы, достаточно осуществить проверку скрипта по типу:

Проверка файла по $_FILES['filename']['type'](429.php)

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Проверка файла по $_FILES['filename']['type']</title>
</head>

<body>
<form action="" method="post" enctype="multipart/form-data">
<input name="filename" type="file" size="30">
<input name="" type="submit" value="Загрузить">
</form>
<?php
//Обработчик формы
if(!empty($_FILES['filename']['tmp_name']))
{
if(substr($_FILES['filename']['type'],0,5)=='image')
{
//Сохраняем файл в текущем каталоге
if(copy($_FILES['filename']['tmp_name'], $_FILES['filename']['name']))
{
echo "Файл успешно загружен - <a href=".$_FILES['filename']['name'].">".$_FILES['filename']['name']. "</a>";
}
}
else
{
//Файл с незарегистрированным расширением
echo "Разрешена загрузка только изображений";
}
}
?>
</body>
</html>

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

Фильтрация загруженных файлов по расширению (430.php)

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Фильтрация загруженных файлов по расширению</title>
</head>

<body>
<form action="" method="post" enctype="multipart/form-data">
<input name="filename" type="file" size="30">
<input name="" type="submit" value="Загрузить">
</form>

<?php
//Обработчик формы
if(!empty($_FILES['filename']['tmp_name']))
{
//Извлекаем из имени файла расширение
$ext=strtolower(strrchr($_FILES['filename']['name'], "."));
//Разрешаем загружать файлы только определенного формата
$extentions=array(".php", ".phtml", ".html", ".htm", ".pl", ".xml", ".inc");
//Проверяем, входит ли расширение файла
//в список запрещенных файлов

if(in_array($ext, $extentions))
{
$pos=strrpos($_FILES['filename']['name'], ".");
$path=substr($_FILES['filename']['name'], 0, $pos).".txt";
}
else
{
$path=$_FILES['filename']['name'];
}
//сохраняем файл в текущем каталоге
if(copy($_FILES['filename']['tmp_name'], $path))
{
echo "Файл успешно загружен - <a href=$path>$path</a>";
}
}
?>

Самой надежной защитой директории является защита средствами Web-сервера Apache. Как правило, файлы, загружаемые пользователем, храняться в отдельном каталоге. В нем можно создать конфигурационный файл .htaccess, при помощи которого можно переопределть обработчик для РНР и Perl-файлов, потребовав от сервера рассматривать их как обычные текстовые файлы.

Примечание. Web-сервер Apache в сети Интернет является стандартным де-факто (около 70% всех серверов работают под его управлением), поэтому решение является достаточно универсальным.

Защита директории средствами Web-сервера Apache

RemoveHandler .php .phtml .pl
AddType text/plain .php .phtml .pl




  • Другие |
назадвверхвперед
Rambler's Top100