通配符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.0
到192.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 BY
和LIMIT
部分所需查询效率高。
正如前面所述,这样假设一次不交叉的块和一个IP。
如果块相交(例如,您同时禁止192.168.1.0/28
和192.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(例如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的想太多
转换通配符从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范围。
在这里,我们看到Quassnoi体现了经验的声音。如果你没有以数字格式存储你的IP地址,你可能会做错了。 – TehShrike