在PHP中比较IP地址和通配符的优化方法?
任何人都知道一种有效且安全的方法,以查看此输入是否:在PHP中比较IP地址和通配符的优化方法?,php,string,performance,Php,String,Performance,任何人都知道一种有效且安全的方法,以查看此输入是否: $_SERVER['REMOTE_ADDR'] 匹配类似于此不一致筛选器数组的内容(注意,200.100.*.*可以表示为200.100.*.*),通配符由*表示: array( '192.168.1.*', '192.168.2.1*', '10.0.0.*', '200.100.*.*', '300.200.*', ) 更新 想法 foreach($instanceSettings['accessControl']
$_SERVER['REMOTE_ADDR']
匹配类似于此不一致筛选器数组的内容(注意,200.100.*.*可以表示为200.100.*.*),通配符由*表示:
array(
'192.168.1.*',
'192.168.2.1*',
'10.0.0.*',
'200.100.*.*',
'300.200.*',
)
更新
想法
foreach($instanceSettings['accessControl']['allowedIpV4Addresses'] as $ipV4Address) {
echo 'Now checking against '.$ipV4Address.'.';
// Compare each octet
$ipV4AddressOctets = String::explode('.', $ipV4Address);
$remoteIpV4AddressOctets = String::explode('.', $_SERVER['REMOTE_ADDR']);
$remoteIpV4AddressIsAllowed = true;
for($i = 0; $i < Arr::size($ipV4AddressOctets); $i++) {
echo 'Comparing '.$ipV4AddressOctets[$i].' against '.$remoteIpV4AddressOctets[$i].'.';
if($ipV4AddressOctets[$i] != $remoteIpV4AddressOctets[$i] && $ipV4AddressOctets[$i] != '*') {
echo 'No match.';
$remoteIpV4AddressIsAllowed = false;
break;
}
}
// Get out of the foreach if we've found a match
if($remoteIpV4AddressIsAllowed) {
break;
}
}
foreach($instanceSettings['accessControl']['allowedIpV4Addresses']作为$ipV4Address){
echo“正在检查“$ipV4Address”。”;
//比较每个八位组
$ipV4AddressOctets=String::explode('.',$ipV4Address);
$remoteIpV4AddressOctets=String::explode('.',$服务器['REMOTE\u ADDR']);
$remoteIpV4AddressIsAllowed=true;
对于($i=0;$i
为什么不使用正则表达式呢
preg_match("((192\\.168\\.1)|(10\\.0\\.0)|(127\\.0\\.0)\\.[012]\\d{0,2}|(\\:\\:1))",$_SERVER['REMOTE_ADDR'])
删除星号,只需执行以下操作:
$ips = array('192.168.1.', '10.0.0.');
foreach ($ips as $ip) {
if (strpos($_SERVER['REMOTE_ADDR'], $ip) === 0) {
// match
}
}
只是为了好玩,我要把它设计得更完美。好吧,除非你有一个相当长的名单来匹配 假设您只使用通配符来表示“我不关心这个八位元”,那么您可以将数组中的每个条目解析为四个值(每个八位元一个)。假设使用-1表示通配符,0–255表示完全匹配该值。(如果您需要比O(n)更好的性能,其中n是匹配列表的大小,那么这里可以使用更好的数据结构,例如trie。)调用此数组L。当然,您只需要执行一次,而不是每次请求 然后,您可以以相同的方式解析远程地址(除非没有通配符)。您还可以在此处发现REMOTE_ADDR的格式不符合预期,现在检查匹配项变得相当简单:
has_match(ip) =
for n in [0 … L.length)
if (-1 == L.n.0 || L.n.0 = ip.0) && (-1 == L.n.1 || L.n.1 == ip.1) && …
return true
return false
(当然,这是伪代码)我还没有做基准测试,但我会选择使用网络硬件/软件使用的方法 将任意*替换为0和255。 将IP转换为整数 因此,如果255.255.255.*变为255.255.255.0和255.255.255.255 然后在这两个IP上执行ip2long功能 然后可以将给定的ip转换为长ip。例如,将255.255.50.51转换为长ip 然后,您可以比较此给定ip的长ip是否在黑名单中转换的长ip之间。如果是,那么它是不允许的,否则它是
$ips = array("ip1", "ip2");
foreach($ips as $ip){
$ip1 = str_replace("*", "0", $ip);
$ip2 = str_replace("*", "255", $ip);
$ip1 = ip2long($ip1);
$ip2 = ip2long($ip2);
$givenip = $_GET["ip"];
$givenip = ip2long($givenip);
if($givenip >= $ip1 && $ip <= $givenip){
echo "blacklist ip hit between {$ip1} and {$ip2} on {$ip}";
}
}
$ips=array(“ip1”、“ip2”);
foreach($ip作为$ip){
$ip1=str_替换(“*”、“0”、$ip);
$ip2=str_替换(“*”、“255”、$ip);
$ip1=IP2长($ip1);
$ip2=ip2长($ip2);
$givenip=$_GET[“ip”];
$givenip=ip2long($givenip);
如果($givenip>=$ip1&&$ip这个选项允许问题中的所有情况加上没有像123.123这样的星号的短遮罩
/**
* Checks given IP against array of masks like 123.123.123.123, 123.123.*.101, 123.123., 123.123.1*.*
*
* @param $ip
* @param $masks
* @return bool
*/
public static function checkIp($ip, $masks)
{
if (in_array($ip, $masks)) {
return true; // Simple match
} else {
foreach ($masks as $mask) {
if (substr($mask, -1) == '.' AND substr($ip, 0, strlen($mask)) == $mask) {
return true; // Case for 123.123. mask
}
if (strpos($mask, '*') === false) {
continue; // No simple matching and no wildcard in the mask, leaves no chance to match
}
// Breaking into triads
$maskParts = explode('.', $mask);
$ipParts = explode('.', $ip);
foreach ($maskParts as $key => $maskPart) {
if ($maskPart == '*') {
continue; // This triad is matching, continue with next triad
} elseif (strpos($maskPart, '*') !== false) {
// Case like 1*, 1*2, *1
// Let's use regexp for this
$regExp = str_replace('*', '\d{0,3}', $maskPart);
if (preg_match('/^' . $regExp . '$/', $ipParts[$key])) {
continue; // Matching, go to check next triad
} else {
continue 2; // Not matching, Go to check next mask
}
} else {
if ($maskPart != $ipParts[$key]) {
continue 2; // If triad has no wildcard and not matching, check next mask
}
// otherwise just continue
}
}
// We checked all triads and all matched, hence this mask is matching
return true;
}
// We went through all masks and none has matched.
return false;
}
}
这一个是基于preg_match()的,您提供了要测试它的模式和主题
/**
* ip_match("172.30.20.*", "172.30.20.162"); // true
* ip_match("172.30.20", "172.30.20.162"); // true; works if incomplete
* ip_match("172.30.*.12", "172.30.20.12"); // true
* ip_match("172.30.*.12", "172.30.20.11"); // false
* ip_match("172.30.20.*", "172.30.20.*"); // true; wildcards in the subject will match with wildcards in the pattern
* ip_match("172.30.20.12", "172.30.*.*"); // false
*
* @param $pattern The pattern to test against as a string
* @param $subject The input string
*
* @return bool
*
*/
function ip_match($pattern, $subject) {
$pattern = explode(".", trim($pattern, "."));
$pattern = array_pad($pattern, 4, "*");
$subject = explode(".", trim($subject, "."));
$subject = array_pad($subject, 4, "1");
foreach ($pattern as $i => $octet) {
if ($octet != "*" && $subject[$i] != $octet) {
return false;
}
}
return true;
}
我想我可以根据数组的内容动态生成一个regex to。我更新了问题,使其更明显地表明筛选器数组是动态的。此解决方案不需要按照问题的要求进行“优化”。通常,IP最好被视为整数(无符号或长)将整数与整数数组进行比较要比将正则表达式数组与字符串(即ip)进行比较快@stereofrog:恐怕我从未使用过Refal(在你提到它之前也没有听说过它)。很可能我读过别人的伪代码,不过。我不太确定它与我的伪代码有什么相似之处……除了下标的
。只写了我想写的(很好的解决方案!)。关于192.168.*.*.
?您还需要从$ip字符串中删除。
。将不会捕获这样的内容:255.*.255.255.+1用于ip2long和数字比较,而不是字符串比较。列表中的第二个ip将破坏您的解决方案:192.168.2.1.
将变成192.168.2.10
和192.168.2.1255
。虽然第一个不是我们想要的,但第二个是完全非法的IP地址:)不应该$IP与@Lekensteyn达成一致——很明显,范围是使用$ip1
和$ip2
确定的(顺便说一句,我会将这些变量的名称更改为$ipStart
和$ipEnd
,以便更清楚地说明这一点)。您是否也可以指定每个ip地址的网络掩码?即:'192.168.100.251/26'
或'192.168.100.251'=>26'
(26位掩码甚至可能对给定的IP地址无效,这只是为了举例)如果您可以指定网络掩码,那么计算IP地址有效性就像If($first\u addr\u of_mask>$IP&$last\u addr\u of_mask<$IP)一样简单。
我不会在PHP中这样做,而是在服务器防火墙上。