1

最近,我的一台服务器被一些中国 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);   
}
  • 基本上,脚本会检查当前 ip 是否在文件“ipBlocked”中找到,如果发现它死了。
  • 如果未找到,则将当前 ip 添加到文件记录器“ipLogger”中。
  • 在此之后,它会计算文件 ipLogger 中 ip 的出现次数,如果它们 > $max 它会通过将 ip 添加到文件 ipBlocked 来阻止该 ip

ATM 正在工作.. 它已经禁止了一些中文/tw ip

这个脚本的瓶颈是搜索功能,它必须计算一个字符串(ip)文件中出现的次数。出于这个原因,我将文件保持在低位(iplogger 文件在记录到 600-700 ips 后立即被截断)

当然要在文件中添加 ips 而不必担心竞争条件,我这样做:

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

我遇到的唯一问题是 NAT 背后的人。他们都有相同的IP,但他们的请求不应该被阻止

4

4 回答 4

3

虽然这是在请求执行更重的操作(如 db 读取等)之前停止请求,但您可能需要考虑将其降低到 Web 服务器的级别,甚至进一步降低到软件/硬件防火墙。

较低的级别将更优雅地处理这个问题,并且开销更少。请记住,通过提出 PHP,他们仍然会在一段时间内消耗您的一名工人。

于 2011-05-18T14:11:12.023 回答
1

这是我的一些笔记,希望对你有用。

在我看来,功能防火墙做的太多了,它的名字也不是很具体。它既可以处理 ip/visits 的保存,也可以结束脚本或什么都不做。我希望在调用这个函数时墙壁会被点燃。

我会采用一种更面向对象的方法,其中防火墙不是命名为防火墙,而是类似于黑名单。

$oBlackList = new BlackList();

这个对象只负责黑名单本身,但仅此而已。它可以判断一个 IP 地址是否在黑名单上,从而实现如下功能:

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

这样,您可以按照自己喜欢的方式进行创作,并且不受函数体的限制。您可以使用函数扩展对象以将地址添加到列表中。$oBlackList->addToList($sIpAddress);也许。

这样,访问量的处理或存储不限于您的防火墙主体。您可以实现数据库存储、文件存储(就像您现在使用的那样)并随时切换而不会使您的黑名单失效。

反正就是乱跑!

于 2011-05-18T14:28:19.013 回答
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 被多次解锁。阻止不需要的请求的一些想法:

  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);

现在您为每个可能是 DoS 攻击的 ip 都有一个文件夹,其中包含microtime其请求,如果您认为它包含许多这些请求,您可以使用touch('firewall/' . $_SERVER['REMOTE_ADDR']). 当然你应该定期清理整个事情。

我使用这种防火墙的经验(德语)非常好。

于 2015-05-06T09:16:40.417 回答
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功能中。

于 2011-05-18T14:19:26.273 回答