Как я могу улучшить этот скрипт брандмауэр? - PullRequest
1 голос
/ 18 мая 2011

В последнее время один из моих серверов получает цели из-за нескольких атак (тысячи запросов / мин) на некоторые китайские IP-адреса (эти ip не всегда одинаковы).

Итак, в начале моегоFramework Я сделал небольшую функцию для блокировки после ip, если он сделал слишком много запросов.

function firewall() {
  $whitelist = array('someips');

  $ip = $_SERVER['REMOTE_ADDR'];

  if (in_array($ip,$whitelist))
    return null;

  if (search($ip,$pathToFileIpBanned))
    die('Your ip did too many requests')

  appendToFile($ip,$pathTofileIpLogger); //< When the file reaches 13000 bytes truncate it

  if (search($ip,$pathTofileIpLogger) > $maxRequestsAllowed)
     appendToFile($ip,$pathToFileIpBanned);   
}
  • В основном скрипт проверяет, найден ли текущий ip в файле 'ipBlocked', если он найденон умирает.
  • Если он не найден, он добавляет текущий ip в файловый логгер 'ipLogger'.
  • После этого он считает количество ip в файле ipLogger, если они>>макс он блокирует этот ip, добавив ip в файл ipBlocked

Банкомат работает .. он запретил некоторые китайские / tw ips

Узким местом этого скрипта является функция поискаэто должно подсчитать вхождения в файл строки (IP).По этой причине я сохраняю низкий уровень файла (файл iplogger усекается, как только он достигает 600-700 зарегистрированных ips)

Конечно, чтобы добавить ips в файл, не беспокоясь о состоянии гонки, я делаю этовот так:

file_put_contents($file,$ip."\n",FILE_APPEND | LOCK_EX);

Единственная проблема, с которой я сталкиваюсь - это человек за NAT.все они имеют одинаковый IP-адрес, но их запросы не должны блокироваться

Ответы [ 4 ]

3 голосов
/ 18 мая 2011

Хотя это останавливает запросы до того, как они выполнят что-то более тяжелое, например чтение базы данных и т. Д., Вы можете рассмотреть возможность передачи этого уровня на веб-сервер или даже дальше на программный / аппаратный брандмауэр.1002 * Более низкие уровни будут справляться с этим гораздо более любезно и с гораздо меньшими накладными расходами.Помните, что, запустив PHP, они еще некоторое время потребляют одного из ваших работников.

1 голос
/ 06 мая 2015

Вы должны создать для каждого заблокированного IP файла.Таким образом вы можете заблокировать посетителя через .htaccess следующим образом:

# redirect if ip has been banned
ErrorDocument 403 /
RewriteCond %{REQUEST_URI} !^/index\.php$
RewriteCond /usr/www/firewall/%{REMOTE_ADDR} -f
RewriteRule . - [F,L]

Как видите, он разрешает доступ только к index.php.Таким образом, вы можете сделать простую file_exists() в первой строке перед выполнением тяжелых запросов к БД и можете бросить капчу разблокировки IP, чтобы избежать постоянной блокировки ложных срабатываний.Тем самым вы получаете лучший пользовательский опыт по сравнению с простым аппаратным брандмауэром, который не возвращает никакой информации или не имеет механизма разблокировки.Конечно, вы могли бы бросить простой текстовый файл HTML (с php-файлом в качестве цели формы), чтобы избежать также работы синтаксического анализатора PHP.

Что касается DoS, я не думаю, что вам следует полагаться только на IP-адреса, так какприведет к множеству ложных срабатываний.Или у вас есть 2-й уровень для прокси ips.Например, если IP был разблокирован несколько раз.Некоторые идеи для блокировки нежелательных запросов:

  1. Это человек или сканер?(HTTP_USER_AGENT)
  2. если сканер, уважает ли он robots.txt?
  3. если человек, он получает доступ к ссылкам, которые не посещаются людьми (например, ссылки, которые сделаны невидимыми через cssили переместился за пределы видимого диапазона или форм ...)
  4. если гусеничный, как насчет белого списка?
  5. если человек, он открывает ссылки, как это сделал бы человек?(пример: в нижнем колонтитуле stackoverflow вы найдете tour help blog chat data legal privacy policy work here advertising info mobile contact us feedback. Ни один человек не откроет 5 или более из них, я думаю, но плохой сканер может = заблокировать свой ip.

Если вы действительно хотитечтобы полагаться на ip / min, я предлагаю не использовать LOCK_EX и только один файл, поскольку это приведет к узкому месту (пока существует блокировка, все другие запросы должны ждать). Вам нужен резервный файл, пока существует LOCK. Пример:

$i = 0;
$ip_dir = 'ipcheck/';
if (!file_exists($ip_dir) || !is_writeable($ip_dir)) {
    exit('ip chache not writeable!');
}
$ip_file = $ip_dir . $_SERVER['REMOTE_ADDR'];
while (!($fp = @fopen($ip_file . '_' . $i, 'a')) && !flock($fp, LOCK_EX|LOCK_NB, $wouldblock) && $wouldblock) {
    $i++;
}
// by now we have an exclusive and race condition safe lock
fwrite($fp, time() . PHP_EOL);
fclose($fp);

Это приведет к созданию файла с именем 12.34.56.78_0, и, если он попадет в узкое место, он создаст новый файл с именем 12.34.56.78_1. Наконец, вам нужно только объединить эти файлы (соблюдайте блокировки!)и проверять множество запросов в данный период времени.

Но теперь вы столкнулись с следующей проблемой. Вам нужно начинать проверку для каждого запроса. Не очень хорошая идея. Простым решением будет использованиеmt_rand(0, 10) == 0 перед началом проверки. Другим решением является проверка filesize(), поэтому нам не нужно открывать файл. Это возможно, поскольку размер файла увеличивается при каждом запросе. Или вы проверяете filemtime().Последнее изменение файла выполняется в ту же секунду или только одну секунду назад.PS Обе функции равны быстро .

И вот я прихожу к своему последнему предложению.Используйте только touch() и filemtime():

$ip_dir = 'ipcheck/';
$ip_file = $ip_dir . $_SERVER['REMOTE_ADDR'];
// check if last request is one second ago
if (filemtime($ip_file) + 1 >= time()) {
    mkdir($ip_dir . $_SERVER['REMOTE_ADDR'] . '/');
    touch(microtime(true));
}
touch($ip_file);

Теперь у вас есть папка для каждого ip, которая может быть DoS-атакой, содержащая microtime ее запроса и, если вы считаете, что она содержит многоэти запросы вы можете заблокировать IP с помощью touch('firewall/' . $_SERVER['REMOTE_ADDR']).Конечно, вам следует периодически очищать все это.

Мой опыт (немецкий) Использование такого брандмауэра очень хорошо.

1 голос
/ 18 мая 2011

Вот мои несколько заметок, надеюсь, вы найдете их полезными.

По моему мнению, функция firewall делает слишком много и не очень конкретна в своем названии. Он оба обрабатывает сохранение ip / посещения, заканчивая скрипт или ничего не делая. Я ожидаю, что стена будет зажжена при вызове этой функции.

Я бы пошел на более объектно-ориентированный подход, где брандмауэр не называется брандмауэром, а что-то вроде черного списка.

$oBlackList = new BlackList();

Этот объект будет отвечать только за сам черный список, но не более того. Можно было бы сказать, если IP-адрес находится в черном списке, таким образом, реализовать такую ​​функцию, как:

$oBlackList = new BlackList();
if ($oBlackList->isListed($sIpAddress)) {
    // Do something, burn the intruder!
}

Таким образом, вы можете проявлять творческий подход так, как вам хотелось бы, и вы не ограничены рамками своей функции. Вы можете расширить объект с помощью функции, чтобы добавить адрес в список. $oBlackList->addToList($sIpAddress); возможно.

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

В любом случае, просто бессвязно!

0 голосов
/ 18 мая 2011

Некоторый очень простой файл / сериализованный код, который вы можете использовать в качестве примера:

<?php
$ip = $_SERVER['REMOTE_ADDR'];

$ips = @unserialize(file_get_contents('%path/to/your/ipLoggerFile%'));
if (!is_array($ips)) {
  $ips = array();
}

if (!isset($ips[$ip])) {
  $ips[$ip] = 0;
}

$ips[$ip] += 1;
file_put_contents('%path/to/your/ipLoggerFile%', serialize($ips));

if ($ips[$ip] > $maxRequestsAllowed) {
  // return false or something
}

Конечно, вам придется каким-то образом интегрировать это в вашу firewall функцию.

...