如何阻止100,000+个人IP地址
介绍
如何从您的Web应用程序/服务器阻止大量的IP address
。 很明显,可以用PHP
或任何编程语言轻松完成
$ipList = []; // array list or from database if (in_array(getIP(), $ipList)) { // Log IP & Access information header("https://www.google.com.ng/search?q=fool"); // redirect exit(); // exit }
或使用htaccess
order allow,deny deny from 123.45.6.7 deny from 012.34.5. # .... the list continues allow from all
问题
- 我试图阻止整个
100k plus individual IPs
而不是subnets
- 我试图避免用户在阻止这样的IP之前进入PHP
- 100000+超过1.5MB,如果信息一直以
htaccess
加载的话,这是很多的 - IP数据库还在不断增长……他们将不得不dynamic地添加更多的价值
- 在
iptables
设置禁止100000+只是荒谬的(可能是错误的)
愚蠢的想法
order allow,deny deny from database <-------- Not sure if this is possible allow from all
题
-
htaccess
有可能从数据库(Redis,Crunchbase,Mongo,MySQL甚至Sqlite)获取列表…任何 - 是否有明显的解决scheme来pipe理生产中的这类问题
- 我知道最好的解决scheme是
Block the IPs at the firewall level
是否有任何方法务实地添加/删除IP到防火墙
最后
我的方法可能是完全错误的…我想要的只是一个可见的解决scheme,因为垃圾邮件发送者和僵尸networking正在崛起。
请这与DOS
攻击没有任何关系…简单的get lost response
更新
- 防火墙: Cisco PIX 515UR
你可以尝试的是保留一个你想在文本文件中阻塞的IP地址列表,或者把它转换成一个dbm哈希文件 ,然后使用mod_rewrite的RewriteMap
。 您必须在您的服务器/虚拟主机configuration中进行设置。 您无法在htaccess文件中初始化映射 。
RewriteEngine On RewriteMap deny_ips txt:/path/to/deny_ips.txt RewriteCond ${deny_ips:%{REMOTE_ADDR}|0} !=0 RewriteRule ^ - [L,F]
/path/to/deny_ips.txt文件看起来像这样:
12.34.56.78 1 11.22.33.44 1 etc.
本质上,你要拒绝的IP和空格然后是“1”。 此文本文件中的任何IP都将导致服务器返回一个403 Forbidden 。 为了加快速度,可以使用httxt2dbm
生成一个dbm哈希,然后你可以这样定义映射:
RewriteMap deny_ips dbm:/path/to/deny_ips.dbm
我不确定使用mod_rewrite有多大的IP,但是在linux下运行在3Ghz i686上的apache 2.2的快速基准testing,列表中的5个IP与102418之间的差异是微不足道的。 根据ab的输出,他们几乎是相同的。
解决具体问题:
htaccess有可能从数据库(Redis,Crunchbase,Mongo,MySQL甚至Sqlite)获取列表…任何
使用重写映射,可以使用“ prg ”映射types为映射types运行外部程序。 然后,您可以编写一个perl,php等脚本来查询数据库,以查找IP地址。 另外请注意“警告”下列出的注意事项。 然后,您会像使用其他地图一样使用此地图( RewriteCond ${deny_ips:%{REMOTE_ADDR}|0} !=0
)。 这实质上会造成所有请求的瓶颈。 不是与数据库交谈的最佳解决scheme。
不过在apache 2.4中,有一个dbd / fastdbd映射types,它允许你通过mod_dbd创build查询。 这是一个更好的select,mod_dbd模块pipe理与数据库,池连接等的连接。因此,地图定义如下所示:
RewriteMap deny_ips "fastdbd:SELECT active FROM deny_ips WHERE source = %s"
假设你有一个表“ deny_ips ”,有两列“ 源 ”(IP地址)和“ 激活 ”(1表示激活,0表示不激活)。
是否有明显的解决scheme来pipe理生产中的这类问题
如果要将所有被阻止的IP存储在数据库中,则需要pipe理数据库表的内容。 如果您使用的是dbm映射types,我知道至lessperl有一个用于pipe理dbm文件的DBI ,因此您可以使用它来从拒绝列表中添加/删除IP条目。 我从来没有使用过,所以我不能说太多。 pipe理一个扁平的文本文件将变得更加棘手,特别是如果你打算删除条目,而不是附加到它。 除了使用数据库和apache 2.4的mod_dbd之外,我不认为这些解决scheme是开箱即用的。 这将需要定制工作。
我知道最好的解决scheme是阻止IP在防火墙级别是否有任何方法务实地添加/删除IP到防火墙
对于IPtables,有一个标记为Beta的perl接口 ,但是我从来没有使用它。 有libiptc,但根据netfilter的常见问题 :
是否有添加/删除规则的C / C ++ API?
答案不幸的是:没有。
现在你可能会想'但是libiptc呢?' 正如在邮件列表中多次指出的那样,libiptc 从来不会被用作公共接口。 我们不保证一个稳定的接口,并且计划在接下来的linux包过滤中将其删除。 无论如何,libiptc的方式太低层,无法合理使用。
我们清楚地意识到,这样的API基本缺乏,我们正在努力改善这种情况。 在此之前,build议使用system()或打开一个pipe道到iptables-restore的stdin。 后者会给你更好的performance。
所以我不知道如果没有API稳定性,libiptc解决scheme是多么可行。
另一个angular度
你好。 您可以通过访问每个8KB长的两个数据块中的两个字节来检查地址是否被阻塞。 是的,我是认真的…请耐心等待,因为需要一点时间来解释它。
理论
IP地址是一个地址,实际上是一个4字节的数字。
问题是,如果我们要解决位位置呢?
答案:好吧,我们会有的
2^32 = 4 Giga Bits
处理空间,将采取
4Gb/8 = 512 Mega Bytes
的分配。 哎哟! 但是不要担心,我们不会阻止ipverse的一切,512MB是夸张的。
这可以为我们打开解决scheme的道路。
Lilliputian案例
考虑一个只有IP地址从0到65535的Lilliputian世界。所以地址是0.1或者42.42到255.255。
现在,这个世界的国王想阻止几个L-IP(lilliput ip)地址。
首先他build立一个256 * 256位长的虚拟2D位图,占用:
64 K Bits = 8 K Bytes.
他决定封杀他所不喜欢的那个讨厌的“革命”地盘,比如地址是56.28。
Address = (56 * 256) + 28 = 14364.(bit position in whole map) Byte in map = floor(14364 / 8) = 1795. Bit position= 14364 % 8 = 4.(modulus)
他打开地图文件,访问第1795个字节并设置位4(由| 16),然后将其写回以将该地点标记为被阻止。
当他的脚本看到56.28时,它执行相同的计算并查看该位,如果它被设置,则阻止该地址。
那么故事的寓意是什么呢? 那么我们可以使用这个lilliputian结构。
实践
真实世界的案例
我们可以将Lilliputian案例应用到现实世界中,因为分配一个512MB的文件并不是一个好的select。
考虑一个名为BLOCKS的数据库表,其条目如下:
IpHead(key): unsigned 16 bit integer, Map : 8KB BLOB(fixed size), EntryCount : unsigned 16 bit integer.
而另一个只有一个入口的表格,下面的结构名为BASE
Map : 8KB BLOB(fixed size).
现在让我们说你有一个传入的地址56.28.10.2
脚本访问BASE表并获取地图。
它查找更高阶的 IP号码56.28:
Address = (56 * 256) + 28 = 14364.(bit position in whole map) Byte in map = floor(14364 / 8) = 1795. Bit position= 14364 % 8 = 4.(modulus)
看地图中的字节1795位4。
如果未设置该位,则不需要进一步操作,这意味着在56.28.0.0 – 56.28.255.255范围内没有阻塞的IP地址。
如果位被设置,那么脚本访问BLOCKS表。
更高阶的IP号码是56.28,它给出14364,所以脚本查询带有索引IpHead = 14364的BLOCKS表。获取logging。 logging应该存在,因为它在BASE标记。
脚本进行低阶 IP地址的计算
Address = (10 * 256) + 2 = 2562.(bit position in whole map) Byte in map = floor(2562 / 8) = 320. Bit position= 2562 % 8 = 2.(modulus)
然后通过查看字段Map的字节320的位2来检查地址是否被阻塞。
任务完成!
Q1:为什么我们使用BASE,我们可以直接用14364查询BLOCKS。
A1:是的,我们可以但是BASE地图查找会比任何数据库服务器的BTREEsearch更快。
Q2: BLOCKS表中的EntryCount字段是什么?
A2:同一条logging在地图字段中被封锁的IP地址数。 所以如果我们解封ip的EntryCount达到0,BLOCKSlogging变得不必要了。 它可以被删除,BASE地图上的相应位将被删除。
恕我直言,这种做法将闪电般快速。 对于blob分配也是每个logging8K。 由于db服务器在单独的文件中保留blob,因此具有4K,8K或4K分页倍数的文件系统将会反应很快。
如果被阻止的地址太分散
那么这是一个问题,这将使数据库BLOCKS表不必要地增长。
但是对于这种情况,替代scheme是使用长度为16777216位的256 * 256 * 256位立方体,等于2097152字节= 2MB。
对于我们前面的例子,更高的Ipparsing是:
(56 * 65536)+(28 * 256)+10
因此,BASE将成为一个2MB的文件,而不是一个数据库表logging,这将打开(fopen等)和位将通过寻求解决(如fseek, 从不读取整个文件内容,不必要的),然后访问下面的结构BLOCKS表:
IpHead(key): unsigned 32 bit integer, (only 24 bit is used) Map : 32 unsigned 8 bit integers(char maybe),(256 bit fixed) EntryCount : unsigned 8 bit integer.
这里是位平面位平面(8K 8K)版本的块检查的php示例代码:
注意:这个脚本可以通过消除几个调用等进一步优化。但是这样写就容易理解。
<? define('BLOCK_ON_ERROR', true); // WARNING if true errors block everyone $shost = 'hosturl'; $suser = 'username'; $spass = 'password'; $sdbip = 'database'; $slink = null; $slink = mysqli_connect($shost, $suser, $spass, $sdbip); if (! $slink) { $blocked = BLOCK_ON_ERROR; } else { $blocked = isBlocked(); mysqli_close($slink); // clean, tidy... } if ($blocked) { // do what ever you want when blocked } else { // do what ever you want when not blocked } exit(0); function getUserIp() { $st = array( 'HTTP_CLIENT_IP', 'REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR' ); foreach ( $st as $v ) if (! empty($_SERVER[$v])) return ($_SERVER[$v]); return (""); } function ipToArray($ip) { $ip = explode('.', $ip); foreach ( $ip as $k => $v ) $ip[$k] = intval($v); return ($ip); } function calculateBitPos($IpH, $IpL) { $BitAdr = ($IpH * 256) + $IpL; $BytAdr = floor($BitAdr / 8); $BitOfs = $BitAdr % 8; $BitMask = 1; $BitMask = $BitMask << $BitOfs; return (array( 'bytePos' => $BytAdr, 'bitMask' => $BitMask )); } function getBaseMap($link) { $q = 'SELECT * FROM BASE WHERE id = 0'; $r = mysqli_query($link, $q); if (! $r) return (null); $m = mysqli_fetch_assoc($r); mysqli_free_result($r); return ($m['map']); } function getBlocksMap($link, $IpHead) { $q = "SELECT * FROM BLOCKS WHERE IpHead = $IpHead"; $r = mysqli_query($link, $q); if (! $r) return (null); $m = mysqli_fetch_assoc($r); mysqli_free_result($r); return ($m['map']); } function isBlocked() { global $slink; $ip = getUserIp(); if($ip == "") return (BLOCK_ON_ERROR); $ip = ipToArray($ip); // here you can embed preliminary checks like ip[0] = 10 exit(0) // for unblocking or blocking address range 10 or 192 or 127 etc.... // Look at base table base record. // map is a php string, which in fact is a good byte array $map = getBaseMap($slink); if (! $map) return (BLOCK_ON_ERROR); $p = calculateBitPos($ip[0], $ip[1]); $c = ord($map[$p['bytePos']]); if (($c & $p['bitMask']) == 0) return (false); // No address blocked // Look at blocks table related record $map = getBlocksMap($slink, $p[0]); if (! $map) return (BLOCK_ON_ERROR); $p = calculateBitPos($ip[2], $ip[3]); $c = ord($map[$p['bytePos']]); return (($c & $p['bitMask']) != 0); } ?>
我希望这有帮助。
如果您对细节有疑问,我会很乐意回答。
您需要使用外部防火墙,而不是使用PHP。 我推荐pfSense或PF 。 我以前使用过它,使用起来非常简单,非常直观,而且非常强大。 这是最好的系统pipe理员的select。 我在FreeBSD上运行它,但是在OpenBSD上也很好。 我是一个Linux的家伙,所以我很难说这个,但不要试图在Linux上运行它。 BSD很容易,你可以很快地弄清楚。
pfSense的一个很棒的function是能够使用脚本进行configuration,并将configuration访问权限制在单个networking接口上(这样只有局域网上的东西才能configuration它)。 它也有一些ID10T级别的function,以防止意外切断自己的访问。
你也应该知道,很多垃圾邮件发送者可以使用像Tor这样的东西来快速切换IP地址。 为了解决这个问题,你应该在你的阻止列表中包含已知的出口节点地址(这个列表可以从不同的地方获得)。
使用iptables和ipset阻止stream量到达www服务器之前。
在INPUT链的filter表中捕获黑名单的IPstream量,假定您的Web服务器位于同一台计算机上。 如果您在路由器上阻止IP,您将需要FORWARD链。
首先创buildipset:
ipset create ip_blacklist hash:ip
IP可以通过以下方式添加:
ipset add ip_blacklist xxx.xxx.xxx.xxx
将ipset匹配规则添加到您的iptables(DROP所有数据包匹配ipset):
iptables --table filter --insert INPUT --match set --match-set ip_blacklist src -j DROP
这将停止www服务器之前的黑名单stream量。
编辑:我有机会查找默认的最大大小,它是65536,所以你将需要调整,以支持100000+条目:
ipset create ip_blacklist hash:ip maxelem 120000
你也可以调整散列大小:
ipset create ip_blacklist hash:ip maxelem 120000 hashsize 16384
(必须是2的幂)
我的经验是ipset查找对我的系统(~45000条目)有微不足道的影响。 网上有很多testing用例。 记忆是一个限制因素。
如果您想要通过代码添加/删除的方法,请查看denyhosts 。 您可以通过代码维护IP列表,也可以修补源代码以从任意位置读取。
有一个名为ipset的netfilter项目,所以你可以添加或删除一个列表,你只需要创build一个规则对这个列表
如果你阻止IP,你应该在防火墙级别做这个(你不希望不受欢迎的IP地址的用户进入你的系统)。 因此,我build议编写一个bash脚本来查询数据库并相应地修改你的防火墙configuration文件(这里假设你需要一个利用存储在你的web数据库中的IP地址的解决scheme – 这里可能是一个更好的地方来存储这样的信息)。
编辑:如果你想添加IP地址的黑名单在PHP级别,@波普里build议,这里是如何使用PHP中的系统调用手册: http : //php.net/manual/en/function.system .PHP
如果您使用的是iptables,那么您需要使用这些命令将IP地址添加到您的黑名单中: http : //www.cyberciti.biz/faq/linux-iptables-drop/
我知道一个方法
它的PHP。 正如你在这个问题的初始阶段提到的那样。
$ipList = []; // array list or from database if (in_array(getIP(), $ipList)) { // Log IP & Access information header("https://www.google.com.ng/search?q=fool"); // redirect exit(); // exit }
我读了你写的东西。 但等一下。
为什么你要这样做,这并不重要,但为什么要这样呢?
我的意思是你为什么要避免访问PHP,因为它的速度或仅仅是因为防止,因为它很难调用所有页面的function? 如果避免一些ip访问php is.avoiding主题来看内容的唯一原因。
所以我有了一个想法,并以这种方式推荐你。
使用一个入口点。
我曾与这个解决scheme。
首先用一个简单的htaccess将所有请求发送到一个称为入口点的页面(如index.php)
有一个简单的重写规则,我会给你。 所以当用户请求时
mysite.com/some/path/page.php or anything
htaccess将执行如下所示的内容而不更改url。 所以用户不会感觉到任何东西。
mysite.com/index.php?r=some/path/page.php
所以每个请求都成为一个具有不同$ _GET ['r']参数的请求。 所以对于evey请求我们将会执行index.php。 现在我们可以在index.php中做这样的事情了
$ipList = []; // array list or from database if (in_array(getIP(), $ipList)) { // Log IP & Access information header("https://www.google.com.ng/search?q=fool"); // redirect exit(); // exit } //if after this execute means the IP is not banned //now we can include file that $_GET['r'] points to include $_GET['r'];
它是如此简单,其真实是如此复杂,但主要思想是一样的。 你怎么看?
看来,我们大多数人都同意在防火墙层面进行封锁 。
您可以有一个程序,监听您的网站ips来阻止并生成一个脚本:
ip = getNextIpToBlock() an = increment_unique_alphanum_generator() script = generate_script(ip, an)
脚本看起来像这样(其中[an]是一个字母数字值,[ip]是你的IP块):
en [enter] *password* [enter] conf t [enter] access-list [an] deny ip [ip] 0.0.0.0 any [enter] access-group [an] in interface outside [enter]
然后,您将此脚本加载到另一个执行远程telnet或ssh呼叫到您的FW CLI的程序。
不要忘记注销,也许每隔100 ips你复制运行configuration开始configuration。
我不知道,但你可能想知道现在什么是你的防火墙的限制。
最好,
对列表中的IP进行地理查找。 我自己的经验表明,大多数恶意(即垃圾邮件)连接都来自中国。 如果你觉得你的情况是一样的,你也没有特别需要去服务中国,那么看看你是否可以在防火墙层面有效地阻止整个国家。
恕我直言,这个问题可以从几个angular度考虑
- 你有一个非常大的唯一IP地址来阻止。 你越早在服务器上阻止他们,你将浪费在他们身上的处理能力就越less。 您可以通过来自PHP的适当系统调用,在防火墙中添加/删除IP。
考虑到上述选项,唯一相关的问题是:
- 他们经常访问吗? =>如果是这样,那么解决scheme(1)是你最好的select。
- 你的防火墙可以有效地处理吗? =>否则,您可能需要考虑其他解决scheme。
.htaccess
将是我的第二select。