Простой способ защиты от классического HTTP DDoS

в 14:17, , рубрики: информационная безопасность, системное администрирование, метки:

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

Как это работает

Бот запрашивает страницу, например habrahabr.ru/search/. Бот не умеет загружать вместе со страницей картинки, скрипты, css и пр. Значит в логе будет отображен запрос к /search/ и всё.
Если на habrahabr.ru/search/ заходит живой человек через браузер, то вместе с /search/ в лог попадет множество картинок, скриптов, css и пр.

Настройка

Mysql

/etc/my.cnf

[mysqld]
local-infile=1 #разрешаем load data
# устанавливаем максимальный размер мемори:
max_heap_table_size=1024M
tmp_table_size=1024M

Под рутом:

UPDATE `mysql`.`user` SET `File_priv` = 'Y' WHERE `user`.`Host` = 'localhost' AND `user`.`User` = 'ИМЯ_ЮЗЕРА_БД'; flush privileges;

sysctl

sysctl.conf подробно с комментариями (linux)

Ram drive

Ram drive нужен для ускорения работы с логами nginx-а.
Добавляем в файл /etc/fstab

tmpfs /var/log/ram_disk tmpfs size=1024m 0 0

Затем

mkdir /var/log/ram_disk
mount -t tmpfs -o size=1024m tmpfs /var/log/ram_disk

Алгоритм

1. Выбор ловушки

Берем на сайте любой статичный и ничем не приметный файл (картинка, css, js и пр.), загружаемый при вызове любой страницы динамики, например habrahabr.ru/styles/fontello/css/habr.css
Этот файл нужно сделать некэшируемым, т.е. добавить рандомный параметр, например <?php echo '/styles/fontello/css/habr.css?'.rand(99999999)?>.
Для справки, по умолчанию opera кладет в локальный кэш картинки на 1 час, css/js на 5 минут.

2. Правим конфиг nginx

# делаем нужный нам формат лога
log_format ddos_log '$remote_addrt$msect$status';

# наша ловушка
location =/styles/1347283218/highlight.css {
	access_log  /var/log/ram_disk/hook_access.log ddos_log;
}

# вся остальная статика
location ~* ^.+.(class|htc|bmp|cur|jpg|jpeg|gif|png|svg|xls|doc|xhtml|js|css|mp3|ogg|mpe?g|avi|flv|zip|gz|bz2?|rar|ico|txt|jar|swf)$ {
	access_log  off;
}

# динамика
location / {
	access_log  /var/log/ram_disk/dynamic_access.log ddos_log;
}

3. Создаем таблицы для логов

ENGINE=MEMORY — чтобы было быстрее.

CREATE TABLE `dinamic_log` (
  `inc` bigint(20) NOT NULL AUTO_INCREMENT,
  `remote_addr` varchar(20) NOT NULL DEFAULT '0',
  `time_local` int(20) NOT NULL DEFAULT '0',
  `status` int(4) NOT NULL DEFAULT '0',
  PRIMARY KEY (`inc`),
  KEY `remote_addr` (`remote_addr`),
  KEY `time_local` (`time_local`)
) ENGINE=MEMORY AUTO_INCREMENT=1 DEFAULT CHARSET=latin1

CREATE TABLE `hook_log` (
  `inc` bigint(20) NOT NULL AUTO_INCREMENT,
  `remote_addr` varchar(20) NOT NULL DEFAULT '0',
  `time_local` int(20) NOT NULL DEFAULT '0',
  `status` int(4) NOT NULL DEFAULT '0',
  PRIMARY KEY (`inc`),
  KEY `remote_addr` (`remote_addr`),
  KEY `time_local` (`time_local`)
) ENGINE=MEMORY AUTO_INCREMENT=1 DEFAULT CHARSET=latin1

Таблица, куда занесем ip поисковых ботов

CREATE TABLE `white` (
  `remote_addr` bigint(20) NOT NULL,
  PRIMARY KEY (`remote_addr`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

Таблица заблокированных

CREATE TABLE `black` (
  `remote_addr` bigint(20) NOT NULL,
  `time_local` int(20) NOT NULL DEFAULT '0',
  PRIMARY KEY (`remote_addr`),
  KEY `time_local` (`time_local`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

4. Главный скрипт

Для простоты понимания написано на php, т.к. этот язык знают почти все. И обработка ошибок убрана также — для простоты понимания.

// файл лога динамики
$dinamic_log = $argv[1];
// файл лога ловушки
$hook_log = $argv[2];
// кол-во запросов к динамике без картинок, после которого будем банить.
$r_stop = $argv[3];
// период за который делаем анализ (храним данные в таблице)
$load_time = $argv[4];
// через сколько сек делаем загрузку лога и анализ
$wait_sec = $argv[5];

function load_log($log, $table) {
	$tmp = '/var/log/ram_disk/tmp_ddos_file';
	// копируем лог во временный файл
	copy ($log, $tmp);
	// чистим лог
	file_put_contents($log, "", LOCK_EX);
	// загружам лог в БД
	mysql_query('LOAD DATA CONCURRENT INFILE "'.$tmp.'" IGNORE INTO TABLE '.$table.' FIELDS TERMINATED BY 't' (`remote_addr`, `time_local`, `status`) SET `remote_addr` = INET_ATON(`remote_addr`)');
	// удалем временный файл
	unlink($tmp);
}

// выполняем в бесконечном цикле
while (true) {
	// загружаем лог динамики
	load_log($dinamic_log, 'dinamic_log');
	
	// загружаем лог ловушки
	load_log($hook_log, 'hook_log');

	// ищем ботов. nginx не дает проверять $status, поэтому 200 и 304 фильтруем здесь.
	$res = mysql_query('SELECT dinamic_log.remote_addr FROM `dinamic_log` WHERE (`status` = 200 OR `status` = 304) AND`remote_addr` NOT IN (SELECT `remote_addr` FROM `hook_log`) AND`remote_addr` NOT IN (SELECT `remote_addr` FROM `white`) GROUP BY `remote_addr` HAVING count(inc)>'.$r_stop);
	while ($row = mysql_fetch_array($res))
	{
		// логируем забаненые ip
		mysql_query('INSERT INTO black(`remote_addr`) VALUES ('.$row['remote_addr'].')');
		// блокируем ip
		switch (PHP_OS) {
			case "FreeBSD":
				system('/sbin/route add -host '.$row['remote_addr'].' 127.0.0.1 -blackhole');
				break;
			case "Linux":
				system('/sbin/ip route add blackhole '.long2ip($row['remote_addr']));
				break;
		}			
	}
	// чистим таблицу от старых логов
	mysql_query('DELETE FROM `log` WHERE `time_local` < (UNIX_TIMESTAMP() - '.$load_time.')');
	// ждем
	sleep($wait_sec);
}

Запускаем:

php ddoshook.php /var/log/ram_disk/dynamic_access.log /var/log/ram_disk/hook_access.log 5 300 3

5. Разбаниваем

$block_time = $argv[1]; // на какое время баним ip.
$res = mysql_query('SELECT `remote_addr` FROM black WHERE time_local < (UNIX_TIMESTAMP() - '.$block_time.')');
while ($row = mysql_fetch_array($res)) {
	// разблокировка ip
	switch (PHP_OS) {
		case "FreeBSD":
			system('/sbin/route delete '.$row['remote_addr']);
			break;
		case "Linux":
			system('/sbin/ip route delete '.long2ip($row['remote_addr']));
			break;
	}	
}

Помещаем в крон

* * * * * /usr/bin/php unban.php 86400

Вот и всё, боты банятся, люди пропускаются.

В следующих выпусках:

  • Как выдерживать syn/udp/icmp flood на пределах возможностей сервера и канала.
  • Как безошибочно определять поисковых ботов, не обращая внимания на юзер-агенты.
  • 7 методов, которые помогли отбить более 1000 http ddos-атак.
  • Как получить профессиональную защиту от ddos-атак, заплатив всего 5$.

Автор: DDoSExpert

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js