Pull to refresh

Простой сканер внедрения кода на PHP

Reading time4 min
Views7.8K
Современные хакеры редко “дефейсят” взломанные сайты, как правило, внедряют сторонний код в скрипты для осуществления дальнейших зловредных действий.

Как часто вы тратили часы, выискивая код, внедрённый в ваши скрипты, после атаки?

Некоторое время назад меня привлекли к администрированию десяти сайтов, расположенных на одном виртуальном хостинге. Сайты крутились на “полуразложившихся” движках, написанных в 2000-2003 годах. Сайты постоянно падали под натиском “скрипт-кидди” и изобиловали внедрёнными “зловредами”. Мои задачи были тривиальны: поддержать работу сайтов, перенести на новые движки или залатать дыры в старых движках.

Всё предельно просто, но в полевых условиях было необходимо анализировать текущее состояние файлов, чтобы не дать возможности внедрить код ни в один из сайтов, так как получение доступа к одному сайту на виртуальном хостинге ставило под прицел и сайты, уже перенесённые на новые версии движков. Реагировать на внедрение кода нужно было молниеносно, а проверять файлы вручную при наличии тысяч файлов не представлялось возможным.

В процессе работы родилось очень простое решение, которым я и хочу поделиться. Хочу оговориться, что данное решение простое и не претендует на гениальность и полноту реализации, но надеюсь, что будет кому-то полезно.

Общий алгоритм работы сканера


Алгоритм работы скрипта прост, он рекурсивно проходит все директории и файлы, начиная с директории, указанной при конфигурации и выводит список файлов, изменившихся за указанный промежуток времени. Вот и всё!

Пользоваться будем следующими функциями:
scandir()
stat()

Данные будем выводить в два столбца таблицы:
Дата модификации
Путь к файлу

Код функции с комментариями:

function scan_tree ($folder, $period, $filter = NULL) {
        $files = scandir ($folder);
        // Получаем массив папок и файлов в текущей папке
        foreach ($files as $file) {
            // В цикле обходим все папки и файлы в дериктории
            if (($file == '.') || ($file == '..') || (is_array ($filter) && in_array ($file, $filter))) continue;
            // Пропускаем текущую папку, родительскую папку и папки из фильтруемых
            $item = $folder.DIRECTORY_SEPARATOR.$file;
            // Формируем полный путь к папке или файлу
            if (is_dir ($item)) {
                // Если текущий элемент - папка, то рекурсивно вызываем функцию сканирования
                scan_tree ($item, $period, $filter);
            } else {
                // Если текущий элемент - файл, то получаем информацию о файле
                $stat_info = stat ($item);
                if (time () - $stat_info['mtime'] < $period) {
                    // Если дата изменения файла не старше заданного отрезка времени, то выводим новую строку в таблицу
                    echo '<tr><td>'.date ("d-m H:i", $stat_info[9]).'</td><td>'.$folder.DIRECTORY_SEPARATOR.$file.'</td></tr>';
                }
            }
        }
    }

Конфигурация

if (!empty($_GET['days'])) $days = intval ($_GET['days']); else $days = 1;
// Получаем через GET отрезок времени

$period = 86400 * $days;
//Пересчитываем в секунды для сравнения с timestamp

//$start_folder = 'D:\www\htdocs';
$start_folder = '/home/www/';
// Задаём начальную директорию сканирования

$filter = array ('cache', 'logs.old', 'logs');
// Список фильтруемых папок, они будут игнорироваться при сканировании

Перед запуском необходимо провести небольшую конфигурацию нашего скрипта, переменной $start_folder задать значение, равное пути к каталогу, с которого мы начнём сканирование.
Скрипт поддерживает некий аналог фильтрации, если вам необходимо проигнорировать некую директорию при сканировании, вы можете передать необязательный параметр $filter — массив названий папок. Сканирование папок из массива производиться не будет.
При запуске мы можем указать GET параметр days=n, чтобы вывести изменённые файлы за n дней.

Запуск скрипта

<table>
    <thead>
    <tr>
        <th title="Дата изменения">Дата</th>
        <th title="Пути файла">Путь к файлу</th>
    </tr>
    </thead>
    <tbody>
    <?php scan_tree ($start_folder, $period, $filter); ?>
    </tbody>
</table>

Возможные улучшения функционала

Простейшая реализация имеет огромное количество недостатков и неудобств, для меня одним из основных недостатков стало отсутствие общей сортировки по дате модификации файла. Данную проблему можно решить путём доработки скрипта, но проще мне показалось решение с подключением jQuery плагина TableSorter. Сканер работает достаточно долго, если файлов много, дальнейшая сортировка на стороне сервера требовала бы дополнительного времени и ресурсов, а jQuery позволяет сортировать таблицу на стороне клиента.

Для этого необходимо подключить плагин TableSorter, добавить нашей таблице следующие id и class.

<table id="data_table" class="tablesorter">

Привязать к нашей таблице обработчик.

$(document).ready(function () {
        $("#data_table").tablesorter();
    }
);

Можно автоматизировать процесс полностью, дописав отправку отчёта на электронную почту и запуская сканер через Cron по расписанию.

Результат работы сканера с плагином TableSorter



Надеюсь, моя статья будет полезна начинающим веб разработчикам и даст пищу для размышлений.

Спасибо и удачи!

Update: Как правильно замечено в комментариях полагаться на дату изменения файла нельзя, так как команда touch() позволяет модифицировать дату.

В работе я использую функционал, встроенный в IDE PHP Stormсинхронизацию локального проекта и удалённого FTP сервера, особых знаний и умений это не требует и отлично подойдет начинающим разработчикам, изменения в файлах выводятся очень наглядно и удобно.
Tags:
Hubs:
Total votes 35: ↑17 and ↓18-1
Comments28

Articles