php .htaccess位置 - 如何阻止100,000多个个人IP地址




apache (10)

介绍

如何从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数据库仍在增长......他们需要动态添加更多值
  • iptables为100000+设置禁令只是荒谬(可能是错的)

愚蠢的想法

order allow,deny
deny from database    <-------- Not sure if this is possible
allow from all

  • 是否有可能htaccess从数据库(Redis,Crunchbase,Mongo,MySQL甚至Sqlite)获取列表...任何
  • 是否有可见的解决方案来管理生产中的此类问题
  • 我知道最好的解决方案是Block the IPs at the firewall level是否有任何方式可以实际添加/删除IP到防火墙

最后

我的方法可能完全错误......我想要的只是一个明显的解决方案,因为垃圾邮件发送者和僵尸网络正在崛起......

请把这与DOS攻击无关,它简单... get lost response

更新

  • 防火墙: Cisco PIX 515UR

Answers

似乎我们大多数人同意阻止防火墙级别

您可以拥有一个程序来监听您的网站,以便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]

然后将此脚本加载到另一个程序,该程序执行对FW CLI的远程telnet或ssh调用。

不要忘记注销,也许每100次ips复制正在运行的配置以启动配置。

我不知道,但您现在可能想知道防火墙有哪些限制。

最好,


您可以尝试的是保留要在文本文件中阻止的IP地址列表或将其转换为dbm哈希文件 ,然后使用mod_rewrite的RewriteMap 。 您必须在server / vhost配置中进行设置。 您无法在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的快速基准测试,列表中的5个IP与102418之间的差异可以忽略不计。 根据ab的输出,它们几乎相同。

解决具体问题:

是否有可能htaccess从数据库(Redis,Crunchbase,Mongo,MySQL甚至Sqlite)获取列表...任何

使用重写映射,您可以使用“ prg ”映射类型为映射类型运行外部程序。 然后,您可以编写perl,php等脚本来与数据库通信,以便查找IP地址。 另请注意“警告”下列出的警告。 然后,您可以像使用任何其他地图一样使用此地图( RewriteCond ${deny_ips:%{REMOTE_ADDR}|0} !=0 )。 这实际上会为所有请求创建瓶颈。 与数据库通信不是最好的解决方案。

但是在apache 2.4中,有一个dbd/fastdbd映射类型,它允许您通过mod_dbd创建查询。 这是一个更好的选择,mod_dbd模块管理与数据库的连接,池连接等。因此,映射定义将类似于:

RewriteMap deny_ips "fastdbd:SELECT active FROM deny_ips WHERE source = %s"

假设您有一个表“ deny_ips ”,其中包含2列“ source ”(IP地址)和“ active ”(1表示活动,0表示非活动)。

是否有可见的解决方案来管理生产中的此类问题

如果要将所有被阻止的IP存储在数据库中,则需要管理数据库表的内容。 如果您使用的是dbm映射类型,我知道至少perl有一个用于管理dbm文件的DBI ,因此您可以使用它来从拒绝列表中添加/删除IP条目。 我之前从未使用它,所以我真的不能说太多。 管理平面文本文件会变得更加棘手,特别是如果您计划删除条目,而不仅仅是附加条目。 除了使用数据库和apache 2.4的mod_dbd之外,我认为这些解决方案都没有开箱即用或生产就绪。 这将需要定制工作。

我知道最好的解决方案是阻止防火墙级别的IP是否有任何方式可以实际添加/删除IP到防火墙

对于IPtables,有一个标记为Beta的perl接口 ,但我以前从未使用它。 有libiptc但根据netfilter的faq

是否有用于添加/删除规则的C / C ++ API?

答案不幸的是:不。

现在你可能会想'但是libiptc呢?'。 正如已经多次在邮件列表中指出的那样,libiptc 从未被用作公共界面。 我们不保证一个稳定的接口,并计划在下一个linux数据包过滤的版本中删除它。 libiptc太低层,无论如何都无法合理使用。

我们很清楚,这种API基本缺乏,我们正在努力改善这种情况。 在此之前,建议使用system()或打开管道到iptables-restore的stdin。 后者将为您提供更好的性能。

所以,如果没有API稳定性,我不知道libiptc解决方案的可行性。


对列表中的IP进行地理查找。 我自己的经验表明,大多数恶意(即垃圾邮件)连接都来自中国。 如果您发现同样情况,并且您没有特别需要为中国服务,请查看您是否可以在防火墙级别有效阻止整个国家/地区。


您需要使用外部防火墙,而不是PHP。 我推荐pfSensePF 。 我之前使用它,它非常易于使用,非常直观,而且非常强大。 它是最好的系统管理员的选择。 我在FreeBSD上运行它,但它也适用于OpenBSD。 我是一个Linux人,所以我很难说这个,但不要试图在Linux上运行它。 BSD很简单,你可以快速搞清楚。

pfSense的一个很棒的功能是能够使用脚本进行配置并限制对单个网络接口的配置访问(这样只有LAN上的东西才能配置它)。 它还具有几个ID10T级别的功能,可以防止您意外切断自己的访问。

您还应该意识到,许多垃圾邮件制造者可以使用Tor之类的东西快速切换IP。 要解决此问题,您应该在阻止列表中包含已知退出节点的地址(此列表可从各个位置获得)。


如果你想通过代码添加/删除方法,请查看denyhosts 。 您可以通过代码维护IP列表,也可以修补源代码以从您想要的任何位置读取。


有一个名为ipset的netfilter项目,所以你可以在列表中添加或删除ip,你只需要针对这个列表创建一个规则

http://ipset.netfilter.org/


另一个观点

你好。 您可以通过访问两个长度为8KB的数据块中的两个字节来检查地址是否被阻止。 是的,我很认真......请耐心等待,因为解释它需要一点时间。

理论

IP地址是一个地址,实际上是一个4字节的数字。

问题是,如果我们要解决位位置怎么办?

答案:好的,我们会的

  2^32 = 4 Giga Bits 

解决空间问题

 4Gb/8 = 512 Mega Bytes

分配。 哎哟! 但不要担心,我们不打算阻止ipverse中的所有内容,512MB是夸大其词。

这可以为我们提供解决方案的途径。

Lilliputian案

想想一个只存在0到65535的ip地址的Lilliputian世界。所以地址就像0.1或42.42到255.255。

现在,这个世界的国王希望阻止几个L-IP(lilliput ip)地址。

首先,他构建了一个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文件不是一个好选择。

想象一个名为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表并获取Map。

它查找更高阶的 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)

查看Map中的字节1795位4。

如果未设置位,则不需要进一步操作,这意味着在56.28.0.0 - 56.28.255.255范围内没有阻塞的IP地址。

如果设置了位,则脚本将访问BLOCKS表。

高阶IP数为56.28,这给出了14364,因此脚本使用索引IpHead = 14364查询BLOCKS表。获取记录。 记录应该存在,因为它标记在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地图查找将比BTREE搜索任何数据库服务器更快。

Q2: BLOCKS表中的EntryCount字段是什么?

A2:它是在同一记录的地图字段中阻止的IP地址计数。 因此,如果我们取消阻止ip并且EntryCount达到0,则BLOCKS记录变得不必要。 它可以被擦除,BASE映射上的相应位将被取消。

恕我直言,这种方法将是闪电般快速。 对于blob分配也是每条记录8K。 由于数据库服务器将blob保留在单独的文件中,因此具有4K,8K或4K分页倍数的文件系统将做出快速反应。

如果被阻止的地址过于分散

那是一个问题,这将使数据库BLOCKS表不必要地增长。

但对于这种情况,替代方案是使用256 * 256 * 256位立方体,其长度为16777216位,相当于2097152字节= 2MB。

对于我们之前的示例,更高的Ip解析是:

(56 * 65536)+(28 * 256)+10      

所以BASE将成为一个2MB文件而不是db表记录,它将被打开(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. 

这是用于块位检查bitplane-bitplane(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。 正如你在这个问题的开头所提到的那样。

$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,因为它的速度或只是因为防止,因为它很难在所有页面调用该函数? 如果避免一些ip访问php的唯一原因是.avoiding主题来查看内容。
所以我有了一个想法,我就是这样给你。
使用一个入口点。

我使用过这个解决方案。

首先使用简单的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'];

它如此简单。它真实的那么复杂。但主要思想是一样的。 你怎么看?


如果你阻止IP,你真的应该在防火墙级别这样做(你不希望用户从不受欢迎的IP地址进入你的系统)。 因此,我建议编写一个bash脚本来查询数据库并相应地修改防火墙配置文件(假设您需要一个利用存储在Web数据库中的IP地址的解决方案 - 很可能是存储此类信息的更好的地方)。

编辑:如果你想在PHP级别的黑名单中添加IP地址,正如@Populus建议的那样,这里有关于如何在PHP中使用系统调用的手册: http://php.net/manual/en/function.system.phphttp://php.net/manual/en/function.system.php

如果你正在使用iptables,这里有你需要用来为你的黑名单添加一个ip地址的命令: http://www.cyberciti.biz/faq/linux-iptables-drop/http://www.cyberciti.biz/faq/linux-iptables-drop/


单引号是字符串的开头和结尾。 分号是语句的结尾。 所以如果他们正在做这样的选择:

Select *
From Students
Where (Name = '<NameGetsInsertedHere>')

SQL会变成:

Select *
From Students
Where (Name = 'Robert'); DROP TABLE STUDENTS; --')
--             ^-------------------------------^

在某些系统上, select将首先运行,然后是drop语句! 该消息是:不要嵌入你的SQL值。 而是使用参数!







php apache security .htaccess