Вы должны создать для каждого заблокированного 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 был разблокирован несколько раз.Некоторые идеи для блокировки нежелательных запросов:
- Это человек или сканер?(
HTTP_USER_AGENT
) - если сканер, уважает ли он
robots.txt
? - если человек, он получает доступ к ссылкам, которые не посещаются людьми (например, ссылки, которые сделаны невидимыми через cssили переместился за пределы видимого диапазона или форм ...)
- если гусеничный, как насчет белого списка?
- если человек, он открывает ссылки, как это сделал бы человек?(пример: в нижнем колонтитуле 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'])
.Конечно, вам следует периодически очищать все это.
Мой опыт (немецкий) Использование такого брандмауэра очень хорошо.