I have an IP-Filter for a whitelist in one of my older projects, which I want to reuse in a new application.
edit for clarification; It works like this:
The whitelist contains entries in a format specified below. Using foreach ($whitelist as $listed)
, I check which type the current entry ($listed
) is, and then compare this entry with $ip
. As soon as it finds an entry, that matches the specified IP, it will return true, if after going through the whole whitelist no match was found it will return false.
As of now, only IPv4 is supported and the filter allows for a whitelist entries like followed:
- IP-range by specifying BEGIN - END (
192.168.0.1-192.168.0.5
) - single IP-address (e.g.
192.168.0.2
) - IP-range using a *-wildcard (e.g.
192.168.0.*
)
The methods to check each of these cases look like this, where $ip
is the client's IP and $listed
is an entry from the whitelist/blacklist matching one of the above mentioned formats:
public function checkAgainstRange($ip, $listed)
{
list($begin, $end) = explode('-', $listed);
$begin = ip2long($begin);
$end = ip2long($end);
$ip = ip2long($ip);
return ($ip >= $begin && $ip <= $end);
}
public function checkAgainstSingle($ip, $listed)
{
return long2ip(ip2long($ip)) === long2ip(ip2long($listed));
}
public function checkAgainstWildcard($ip, $listed)
{
$listedSegment = explode('.', $listed);
$ipSegment = explode('.', $ip);
for ($i = 0; $i < count($listedSegment); $i++) {
// We're finished when the wildcarded block is reached
// Important: Check for wildcard first, as it won't match the client IP!
if ($listedSegment[$i] == '*') {
return true;
}
if ($listedSegment[$i] != $ipSegment[$i]) {
return false;
}
}
// Just to be safe: If we reach this, something went wrong
return false;
}
I need some directions as to how to make these work with IPv6-addresses.
Some of the required changes are obvious:
* ip2long()
only works with IPv4-addresses
* I have to check for :
as a possible delimiter in checkAgainstWildcard()
.
I found inet_ntop()
and inet_pton()
in the php-docs. Can I just use the following to compare two single ip-addresses?
public function checkAgainstSingle($ip, $listed)
{
$ip = inet_ntop($ip);
$listed = inet_ntop($listed);
if ($ip === false || $listed === false) {
throw new \Exception;
}
return $ip === $false;
}
Usage examples:
$filter = new IpFilter();
$ip = $_SERVER['REMOTE_ADDR'];
$result = $filter->checkAgainstSingle($ip, '192.168.0.1');
$result = $filter->checkAgainstRange($ip, '192.168.0.1-192.168.0.10');
$result = $filter->checkAgainstWildcard($ip, '192.168.0.*');
Is it even useful to keep something like checkAgainstRange()
? And if so, how could I even check for IPv6-ranges in a similar way? Obviously I cant't change ip2long()
to inet_ntop()
here...
Same goes for wildcard-ranges. Should I keep them and would it suffice to check for :
as a segment delimiter and if it is not found, fall back to .
as delimiter?