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:
2026-03-11 08:54:40 +02:00
parent 2b4591c49f
commit 1dc04326aa

31
api.php
View File

@@ -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;