最近,我的一台服务器正在通过一些中国IP(这些ip并不总是相同)进行多次dos攻击(数千请求/分钟)。
所以在我的框架开始时,如果它提出了太多请求,我在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);
}
ATM正在运行..它禁止了一些中文/ tw ips
此脚本的瓶颈是搜索功能,必须计算字符串文件(ip)中的出现次数。出于这个原因,我保持文件低(iplogger文件一旦达到600-700 ips就被截断)
当然要将ips添加到文件而不必担心竞争条件,我会这样做:
file_put_contents($file,$ip."\n",FILE_APPEND | LOCK_EX);
我遇到的唯一问题是NAT背后的人。它们都具有相同的IP,但不应阻止它们的请求
答案 0 :(得分:3)
虽然这会在他们做任何更重的事情之前停止请求,比如数据库读取等,但您可能需要考虑将其降低到Web服务器的水平,甚至更进一步到软件/硬件防火墙。
较低级别将更加优雅地处理这一问题,并且开销更少。记住,通过提出PHP,他们仍然会在一段时间内消耗你的一名工人。
答案 1 :(得分:1)
以下是我的几点说明,希望你发现它们很有用。
在我看来,功能防火墙做得太多,而且名称不是很具体。它既处理ip / visits的保存,也结束脚本或什么都不做。我打算在调用这个功能时将墙壁设置为火焰。
我会采用更加面向对象的方法,防火墙不是防火墙,而是黑名单。
$oBlackList = new BlackList();
这个对象只对黑名单本身负责,但仅此而已。 它可以说一个IP地址是否在黑名单中,从而实现如下功能:
$oBlackList = new BlackList();
if ($oBlackList->isListed($sIpAddress)) {
// Do something, burn the intruder!
}
通过这种方式,您可以按照自己喜欢的方式进行创作,并且不仅限于功能主体。您可以使用函数展开对象以将地址添加到列表中。或许$oBlackList->addToList($sIpAddress);
。
这样,访问量或其存储的处理不仅限于您的防火墙主体。您可以实现数据库存储,文件存储(正如您现在使用的那样)并随时切换而不会使您的黑名单失效。
无论如何,只是漫无目的!
答案 2 :(得分:1)
您应该为每个被阻止的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
?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()
。如果最后一次文件更改是在同一秒或仅一秒前完成的。附: Both functions are equal fast
由此我得出了最后的建议。仅使用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都有一个文件夹可能是包含其microtime
请求的DoS攻击,如果您认为它包含许多请求,您可以使用touch('firewall/' . $_SERVER['REMOTE_ADDR'])
来阻止IP 。当然你应该定期清理整件事。
我使用这种防火墙的experiences (German)非常好。
答案 3 :(得分:0)
一些非常基本的文件/序列化代码,您可以将其用作示例:
<?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
函数中。