您应该为每个被阻止的 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 地址,因为这会导致许多误报。或者您有第二级将代理 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);
现在您为每个可能是 DoS 攻击的 ip 都有一个文件夹,其中包含microtime
其请求,如果您认为它包含许多这些请求,您可以使用touch('firewall/' . $_SERVER['REMOTE_ADDR'])
. 当然你应该定期清理整个事情。
我使用这种防火墙的经验(德语)非常好。