我有一个IP范围列表,如
$whitelist=array('50*','202.16*','123.168*',');
我想阻止所有其他流量看到该页面。
我试过
if(in_array($_SERVER['REMOTE_ADDR'],$whitelist)){
//display page
}
答案 0 :(得分:3)
in_array
不使用正则表达式进行比较。你的正则表达式也是错误的*
是量词。这允许零个或多个前一个字符。
尝试:
$whitelist=array('50\..*','202\.16\..*','123\.168\..*');
if(preg_match('/^(' . implode('|', $whitelist) . ')/', $_SERVER['REMOTE_ADDR'])){
.*
允许任何内容(非常多(请参阅s
修饰符http://php.net/manual/en/reference.pcre.pattern.modifiers.php),.
是任何字符,然后与之前提到的量词配对。) / ^
是字符串的开头。
\.
是文字.
。
|
是一个或。
演示:https://eval.in/571019
正则表达式演示:https://regex101.com/r/dC5uI0/1
答案 1 :(得分:1)
这应该适合你:
你可以遍历白名单ip并修剪空格和*
(如果从右边找到)。
使用substr
之后,您可以在循环中剪切相同长度的白名单ip的IP地址,并比较两者。
$whitelists = array('50*','202.16*','123.168*');
foreach($whitelists as $whitelist){
$whitelist = rtrim($whitelist, "*\r\n\t\0 ");
if(substr($_SERVER['REMOTE_ADDR'], 0, strlen($whitelist)) == $whitelist) {
$match = true;
break;
}
}
echo $match ? 'Match' : 'Not Match';
答案 2 :(得分:1)
如@ chris85所述,in_array不使用正则表达式。
要做这样的事情,你可以简单地使用这样的循环:
if(preg_match('/^(' . implode('|', $whitelist) . ')/i', $_SERVER['REMOTE_ADDR'])){
// Do your stuff
}
你的'*'没有按照你的想法工作..没关系:
$whitelist=array('50\.*','202.16\.*','123.168\.*');
答案 3 :(得分:0)
希望这对某人有帮助。看来您没有特别询问正则表达式,并且由于该主题是关于匹配IP地址的,所以我想把它放在那儿,以便对遇到类似问题的人有所帮助。
服务器软件通常会力求尽可能快和高效。匹配IP地址通常是通过算术完成的。话虽这么说,我会在找到可能的替代方案之前,先通过一种快速的方法来准确地完成您要尝试的操作。
如果您只是对IP地址字符串执行通配符匹配,建议您使用此方法。它已针对您的用例进行了量身定制,但我将单独包括简单的匹配功能。
与之相反,与使用PHP的RegEx函数相比,我还将使用这种方法的示例输出与执行时间包括在内(对于复杂性而言,这是 运行方式 模式匹配)
注意:
代码(您可以复制和粘贴):
<?php
/**
* This function compares a string ("$test") to see if it is
* equal to another string ("$wild"). An '*' in "$wild" will
* match any characters in "$test".
*
* @param string $wild - The string to compare against. This may
* be either an exact character string to match, or a string
* with a wild card ('*') character that will match any character(s)
* found in "$test".
*
* @param string $test - A character string we're comparing against
* "$wild" to determine if there is a match.
*
* @return bool Returns TRUE if "$test" is either an exact match to
* "$wild", or it fits the bill taking any wild card characters into
* consideration.
*
**/
function wildcard_match( $pattern, $test ) {
$p = 0;
$a_name = explode("*", $pattern);
$segs = count($a_name);
$max_seg = ($segs-1);
$plen = 0;
$test_len = strlen($test);
for ($i = 0; $i < $segs; $i++) {
$part = $a_name[$i];
$plen = strlen($part);
if ($plen === 0) {
if ($i === $max_seg) return true;
continue;
}
$p = strpos($test, $part, $p);
if ($p === false) {
return false;
}
$p+=$plen;
}
if ($p===$test_len) {
return true;
}
return false;
}
/**
* Function to quickly traverse an array of whole, or
* wild card IPv4 addresses given in "$whitelist" and
* determine if they match the given whole IPv4
* address in "$test".
*
* @param array $whitelist - An array of IPv4 addresses, either
* whole, or containing an '*' character wherein any character(s)
* in "$test" will match.
*
* @param string $test - A complete string (dot-decimal) IPv4
* address to compare against the contents of the array given in
* parameter one ("$whitelist").
*
* @return bool Returns TRUE, if the IPv4 address given in "$test" was
* matched to an IPv4 address or IPv4 wild card pattern in the array
* given in parameter one ("$whitelist").
*
**/
function match_ip( $whitelist, $test ) {
foreach ($whitelist as $w) {
if (wildcard_match($w, $test)) return true;
}
return false;
}
/* The array of IP addresses we're going to validate */
$check_array = array("50.245.1.9", "35.125.25.255", "202.16.15.25");
/* The array as given in your example (minus the extra ' at the end) */
$whitelist1=array('50*','202.16*','123.168*');
/* An array for RegEx matching */
$whitelist2=array('50\..*','202\.16\..*','123\.168\..*');
microtime(true); /* Execute this once to make sure its module is loaded */
echo "Giving PHP a second to get its ducks in a row...\n";
usleep(1000000); /** Give PHP a second to load and prepare */
$st = microtime(true);
foreach ($check_array as $c) {
if (match_ip($whitelist1, $c)) {
echo "$c....Match!\n";
} else {
echo "$c....No match!\n";
}
}
$dt = microtime(true)-$st;
echo "Time: $dt\n\n\n\n";
$st = microtime(true);
foreach ($check_array as $c) {
if(preg_match('/^(' . implode('|', $whitelist2) . ')/', $c)){
echo "$c....Match!\n";
} else {
echo "$c....No Match!\n";
}
}
$dt = microtime(true)-$st;
echo "Time: $dt\n\n";
输出为:
Giving PHP a second to get its ducks in a row...
50.245.1.9....Match!
35.125.25.255....No match!
202.16.15.25....Match!
Time 1: 0.00027704238891602
50.245.1.9....Match!
35.125.25.255....No Match!
202.16.15.25....Match!
Time 2: 0.00040698051452637
第一个结果集来自“ match_ip()”函数,第二个结果集来自启动RegEx库。
现在,通配符匹配IP的一种可能更好的解决方案是在数组中采用CIDR表示法的IP地址数组。通常,您希望允许来自特定网络或IP地址范围的IP通信。
这里有很多假设,例如(使用“ $ whitelist”数组):
“ 50 *”可能被解释为“我希望允许访问50.xxx.xxx.xxx中的所有IP地址。
在这种情况下,您将指定格式“ 50.0.0.0/8”。 (“ 50”后面的“ 0”可以是任何数字。由于“ / 8”,它们将被完全忽略。)
xxx.xxx.xxx.xxx
| | | |
8 16 24 32
IPv4地址在计算上是32位,因此,上面所说的是,您关心的只是前8位匹配。
'123.168 *'将为“ 123.168.0.0/16”
“ 101.23.54.0/24”将允许所有以“ 101.23.54”开头的IP地址访问。
“ 44.32.240.10/32”将仅 允许IP地址“ 44.32.240.10”访问。没有范围。
因此您可以执行以下操作:
<?php
/**
* Determines if the two given IPv4 addresses
* are equal, or are on the same network using
* the given number of "$mask" bits.
*
* @param string $ip1 - The first string dot-decimal IPv4
* address.
*
* @param string $ip1 - The second string dot-decimal IPv4
* address.
*
* @param int $mask - The number of bits in the mask.
*
* @return bool Returns TRUE if they match after being
* masked, or FALSE if not.
*
*/
function ip_match( $ip1, $ip2, $mask) {
$mask = (int)$mask;
$ip1 = ip2long($ip1);
$ip2 = ip2long($ip2);
if ($ip1 === false || $ip2 === false) return false;
if ($mask < 1 || $mask > 32) return false;
$mask = (0x00000000FFFFFFFF & 0x00000000FFFFFFFF << (32-$mask));
if ( ($ip1 & $mask) === ($ip2 & $mask) ) {
return true;
}
return false;
}
/**
* Takes an array of string (CIDR) network representations and
* sorts them into an array used later for checking against IP
* addresses.
*
* @param array $cidr_array - An array of IP addressess in
* CIDR notation e.g. 192.168.1.1/24
*
* @return array Returns an array of objects with the following
* properties:
* 'ip' - The string (dot-decimal) IP address that
* has been numerically verified for use
* in comparisons later.
*
* 'mask' - The number of bits used for creating
* the subnet mask against which IP
* addresses will be compared.
*
**/
function make_whitelist( $cidr_array ) {
$wl = array();
$lip = 0;
$bm = 0;
$spos = 0;
if (!is_array($cidr_array)) return false;
foreach ($cidr_array as $ip) {
$spos = strpos($ip, "/");
if ($spos === false) {
$bm = 32; /* If there's no "/", assume
* that we want an EXACT IP
* address. Hence the 32 bit
* mask
**/
} else {
$bm = (int)substr($ip, ($spos+1));
$ip = substr($ip, 0, $spos++);
}
$lip = ip2long($ip); /* Using this here to check IP validity
* before storing it in the array...
* We use ip2long() later for comparisons.
*
* You can store it this way - as a long -
* instead of as a string (I do) to
* use less memory if you wish.
*
**/
if ($bm === 0) continue; /* A bit mask of ZERO will block
* ALL IP addresses, skip it
* for the example.
**/
if ($lip === false) continue; /* If it's an invalid IP, skip it,
* you could optionally try to
* resolve it as a hostname using
* gethostbyname() or gethostbynamel()
* here...
**/
array_push($wl, (object)array('ip'=>$ip, 'mask'=>$bm));
}
return $wl;
}
$whitelist = make_whitelist(array("50.0.0.0/8", "202.16.0.0/16", "123.168.0.0/16", "1.1.1.1"));
$ips_to_check = array("50.1.174.41", "42.123.100.23", "123.168.4.79", "1.1.1.2", "1.1.1.1");
foreach ($ips_to_check as $ip) {
foreach ($whitelist as $w) {
if (ip_match($ip, $w->ip, $w->mask)) {
echo "\"$ip\" is allowed!\n";
continue 2;
}
}
echo "\"$ip\" is NOT allowed!\n";
}
我知道很多,但是这里有很多可供人们思考,寻找和寻找的地方,希望能够使他们的生活更轻松!