IPv6-tuki IP-rajoitukseen
Vaihdettu ip2long() -> inet_pton() joka tukee sekä IPv4 että IPv6. CIDR-alueet toimivat molemmilla (esim. 2001:db8::/32). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
31
api.php
31
api.php
@@ -140,29 +140,36 @@ function getClientIp(): string {
|
|||||||
/**
|
/**
|
||||||
* Tarkista onko IP sallittujen listalla.
|
* Tarkista onko IP sallittujen listalla.
|
||||||
* Tyhjä lista = ei rajoitusta (kaikki sallittu).
|
* Tyhjä lista = ei rajoitusta (kaikki sallittu).
|
||||||
* Tukee yksittäisiä IP-osoitteita ja CIDR-alueita (esim. 192.168.1.0/24).
|
* Tukee IPv4 ja IPv6, yksittäisiä osoitteita ja CIDR-alueita.
|
||||||
*/
|
*/
|
||||||
function isIpAllowed(string $ip, string $allowedIps): bool {
|
function isIpAllowed(string $ip, string $allowedIps): bool {
|
||||||
$allowedIps = trim($allowedIps);
|
$allowedIps = trim($allowedIps);
|
||||||
if ($allowedIps === '' || strtolower($allowedIps) === 'kaikki') return true; // ei rajoitusta
|
if ($allowedIps === '' || strtolower($allowedIps) === 'kaikki') return true;
|
||||||
$entries = preg_split('/[\s,]+/', $allowedIps, -1, PREG_SPLIT_NO_EMPTY);
|
$entries = preg_split('/[\s,]+/', $allowedIps, -1, PREG_SPLIT_NO_EMPTY);
|
||||||
$ipLong = ip2long($ip);
|
$ipBin = @inet_pton($ip);
|
||||||
if ($ipLong === false) return false;
|
if ($ipBin === false) return false;
|
||||||
foreach ($entries as $entry) {
|
foreach ($entries as $entry) {
|
||||||
$entry = trim($entry);
|
$entry = trim($entry);
|
||||||
if ($entry === '') continue;
|
if ($entry === '') continue;
|
||||||
if (strpos($entry, '/') !== false) {
|
if (strpos($entry, '/') !== false) {
|
||||||
// CIDR-alue (esim. 192.168.1.0/24)
|
// CIDR-alue (IPv4 tai IPv6)
|
||||||
[$subnet, $bits] = explode('/', $entry, 2);
|
[$subnet, $bits] = explode('/', $entry, 2);
|
||||||
$bits = (int)$bits;
|
$bits = (int)$bits;
|
||||||
if ($bits < 0 || $bits > 32) continue;
|
$subnetBin = @inet_pton($subnet);
|
||||||
$subnetLong = ip2long($subnet);
|
if ($subnetBin === false) continue;
|
||||||
if ($subnetLong === false) continue;
|
// IPv4 = 4 tavua, IPv6 = 16 tavua — pitää olla sama perhe
|
||||||
$mask = $bits === 0 ? 0 : (~0 << (32 - $bits));
|
if (strlen($ipBin) !== strlen($subnetBin)) continue;
|
||||||
if (($ipLong & $mask) === ($subnetLong & $mask)) return true;
|
$maxBits = strlen($ipBin) * 8;
|
||||||
|
if ($bits < 0 || $bits > $maxBits) continue;
|
||||||
|
// Rakenna bittimask
|
||||||
|
$mask = str_repeat("\xff", intdiv($bits, 8));
|
||||||
|
if ($bits % 8) $mask .= chr(0xff << (8 - ($bits % 8)));
|
||||||
|
$mask = str_pad($mask, strlen($ipBin), "\x00");
|
||||||
|
if (($ipBin & $mask) === ($subnetBin & $mask)) return true;
|
||||||
} else {
|
} else {
|
||||||
// Yksittäinen IP
|
// Yksittäinen IP (IPv4 tai IPv6)
|
||||||
if (ip2long($entry) === $ipLong) return true;
|
$entryBin = @inet_pton($entry);
|
||||||
|
if ($entryBin !== false && $ipBin === $entryBin) return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user