通配符IP与MySQL禁止

问题描述:

我试图使用MySQL来实现一个IP禁止系统到我的网络应用程序,我知道我可以使用.htaccess来做到这一点,但这对我来说并不整洁。通配符IP与MySQL禁止

基本上我现在的表是:

ip_blacklist(id, ip, date) 

,并在PHP我仰望数据库客户端或IP来看看它的封锁并不:

$sql = "SELECT ip FROM ip_blacklist WHERE ip = ? LIMIT 1" 
$query = $this->db->query($sql, array($ip)); 
if($query->num_rows() > 0){ 
// Gotcha 
} 

现在..这是工作很好,但我希望能够进入通配符IP范围在数据库中,如:

42.21.58.* 
42.21.*.* 
53.*.*.* 

如何做到这一点?

在此先感谢。

如果你将永远是检查一个IP地址在时间你禁止的范围从不相交,你应该存储范围的开始和结束地址以数字格式禁止。

说,你想要禁止192.168.1.0192.168.1.15这就是192.168.1.0/28

您创建一个表是这样的:

CREATE TABLE ban (start_ip INT UNSIGNED NOT NULL PRIMARY KEY, end_ip INT UNSIGNED NOT NULL) 

,插在那里的范围:

INSERT 
INTO ban 
VALUES (INET_ATON('192.168.1.0'), INET_ATON('192.168.1.0') + POWER(2, 32 - 28) - 1) 

然后检查:

SELECT (
     SELECT end_ip 
     FROM ban 
     WHERE start_ip <= INET_ATON('192.168.1.14') 
     ORDER BY 
       start_ip DESC 
     LIMIT 1 
     ) >= INET_ATON('192.168.1.14') 

ORDER BYLIMIT部分所需查询效率高。

正如前面所述,这样假设一次不交叉的块和一个IP。

如果块相交(例如,您同时禁止192.168.1.0/28192.168.1.0/24),则该查询可能会返回错误的否定结果。

如果你想同时查询多个IP(比如,更新IP地址一长串的表),那么这个查询将是无效的(MySQL不优化相关子查询range井)

在这两种情况下,你应该需要你的范围存储为LineString和使用空间索引快速搜索:

CREATE TABLE ban (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, range LINESTRING NOT NULL) ENGINE=MyISAM; 

CREATE SPATIAL INDEX sx_ban_range ON ban (range); 

INSERT 
INTO ban (range) 
VALUES (
     LineString 
       (
       Point(INET_ATON('192.168.1.0'), -1), 
       Point(INET_ATON('192.168.1.0') + POWER(2, 32 - 28) - 1), 1) 
       ) 
     ); 

SELECT * 
FROM ban 
WHERE MBRContains(range, Point(INET_ATON('192.168.1.14'), 0)) 
+0

在这里,我们看到Quassnoi体现了经验的声音。如果你没有以数字格式存储你的IP地址,你可能会做错了。 – TehShrike

这是棘手的,如果你想禁止子网

注:

  • “通配符” 映射应该用0(例如42.21.58.0),它定义了子网
  • .0可能不会是因为CIDR(能0.128,.192等)

所以子网:

  • 存储IP作为一个4字节的二进制或无符号整型
  • 商店子网掩码为二进制(或uint)4用于子网黑名单
  • INET_NTOA and INET_ATON用于翻译IP地址

然后WHERE条款成为

WHERE ip = @ip --whole IP 
     OR 
     (ip & mask = @ip) --subnet 

如果你把面具0xffffffff的确切IP地址,那么你可以随时做ip & mask = @ip,与ip & mask为计算列

此外,你必须IPv6的想太多

+0

谢谢你,我不太了解IP,但什么是子网标记?以及如何将IP存储为4字节二进制文件? – Ryan

+0

@David:我会开始在维基百科上探索,例如http://en.wikipedia.org/wiki/CIDR。我只是更新了MySQL函数来使用 – gbn

+0

我很确定这里的专家能够理解你的解决方案,但对于我和其他初学者来说,它有点模糊:/你能否给出一个快速的php例子来说明如何将IP转换为你的SQL接受? – Ryan

Quick'n'Dirty的,但不能用正确的索引:

SELECT ip FROM ip_blacklist WHERE ? LIKE REPLACE(ip,'*','%') LIMIT 1 
+1

嗯。其实'?'是客户的IP,不包含星星 – Ryan

+0

这个想法很好。只需将** ip **与** ** **交换即可! :) – Karolis

+0

啊,该死的,错过了,现在修复:) – Wrikken

转换通配符从42.21.*.*42.21.0.0和回写或从数据库中读取条目时。为了提高效率(低内存和磁盘占用空间,性能)将其存储为整数,请使用INET_NTOA and INET_ATON进行转换。

当你看到一个IP地址A.B.C.D:

SELECT ip FROM ip_blacklist WHERE ip=INET_ATON('a.b.c.d') or ip=INET_ATON('a.b.c.0') or ip=INET_ATON('a.b.0.0') or ip=INET_ATON('a.0.0.0') 

好了,最后一场比赛可能是

不要忘记添加索引。

我的建议可能作出一些畏缩,但你似乎在这个项目中去超越传统所以这里有云: 分析每个IP从数据库到可以对用户的IP进行比较的正则表达式。例如:

<?php 
//Fetch IP's and begin to contruct regex 
$regex = array(); 
while($arr = mysql_fetch_array($result)) { 
    $regex[] = '('.$arr['ip'].')'; 
} 

$regex = implode('|', $regex); //Regex now becomes (1.1.1.1)|(2.2.2.2)|etc. 
$regex = str_replace('.', '\.', $regex); //Escape dots for regex 
$regex = str_replace('*', '((25[0-5])|(2[0-4]\d)|(1\d\d)|(\d\d?))', $regex); //Deal with wildcards 

$httpVars = array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR'); 
foreach($httpVars as $httpVar) { //No hiding behind proxies 
    if(isset($IP = $_SERVER[$httpVar])) { 
     break; 
    } 
} 

if(preg_match('/^'.$regex.'$/', $IP) != 0) { 
    die(header('HTTP/1.1 403 Forbidden')); //Magical regex says user should be banned 
} 
?> 

当然,你可以用这个做更多。您可以缓存正则表达式以保存每个请求上的数据库查询,甚至可以扩展通配符选项以包含IP范围。