Korjaa IPv6 IP-allow: IPv4-mapped IPv6 ↔ IPv4 cross-matching
Kun palvelin käyttää IPv6:ta, client IP voi tulla muodossa ::ffff:192.168.1.1 vaikka allow-listassa on 192.168.1.1. Nyt isIpAllowed() tunnistaa IPv4-mapped IPv6 -osoitteet ja vertailee molemmissa muodoissa (IPv4 ↔ ::ffff:IPv4). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
41
api.php
41
api.php
@@ -146,8 +146,22 @@ function isIpAllowed(string $ip, string $allowedIps): bool {
|
|||||||
$allowedIps = trim($allowedIps);
|
$allowedIps = trim($allowedIps);
|
||||||
if ($allowedIps === '' || strtolower($allowedIps) === 'kaikki') return true;
|
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);
|
||||||
|
|
||||||
|
// Normalisoi IP: IPv4-mapped IPv6 (::ffff:1.2.3.4) → myös IPv4 muotoon
|
||||||
$ipBin = @inet_pton($ip);
|
$ipBin = @inet_pton($ip);
|
||||||
if ($ipBin === false) return false;
|
if ($ipBin === false) return false;
|
||||||
|
|
||||||
|
// Jos IP on IPv4-mapped IPv6 (::ffff:x.x.x.x), kokeile myös puhtaana IPv4:nä
|
||||||
|
$ipv4Equivalent = null;
|
||||||
|
if (strlen($ipBin) === 16 && substr($ipBin, 0, 12) === "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff") {
|
||||||
|
$ipv4Equivalent = inet_ntop(substr($ipBin, 12));
|
||||||
|
}
|
||||||
|
// Jos IP on puhdas IPv4, kokeile myös mapped-muodossa
|
||||||
|
$ipv6MappedBin = null;
|
||||||
|
if (strlen($ipBin) === 4) {
|
||||||
|
$ipv6MappedBin = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff" . $ipBin;
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($entries as $entry) {
|
foreach ($entries as $entry) {
|
||||||
$entry = trim($entry);
|
$entry = trim($entry);
|
||||||
if ($entry === '') continue;
|
if ($entry === '') continue;
|
||||||
@@ -157,19 +171,34 @@ function isIpAllowed(string $ip, string $allowedIps): bool {
|
|||||||
$bits = (int)$bits;
|
$bits = (int)$bits;
|
||||||
$subnetBin = @inet_pton($subnet);
|
$subnetBin = @inet_pton($subnet);
|
||||||
if ($subnetBin === false) continue;
|
if ($subnetBin === false) continue;
|
||||||
// IPv4 = 4 tavua, IPv6 = 16 tavua — pitää olla sama perhe
|
$maxBits = strlen($subnetBin) * 8;
|
||||||
if (strlen($ipBin) !== strlen($subnetBin)) continue;
|
|
||||||
$maxBits = strlen($ipBin) * 8;
|
|
||||||
if ($bits < 0 || $bits > $maxBits) continue;
|
if ($bits < 0 || $bits > $maxBits) continue;
|
||||||
// Rakenna bittimask
|
// Rakenna bittimask
|
||||||
$mask = str_repeat("\xff", intdiv($bits, 8));
|
$mask = str_repeat("\xff", intdiv($bits, 8));
|
||||||
if ($bits % 8) $mask .= chr(0xff << (8 - ($bits % 8)));
|
if ($bits % 8) $mask .= chr(0xff << (8 - ($bits % 8)));
|
||||||
$mask = str_pad($mask, strlen($ipBin), "\x00");
|
$mask = str_pad($mask, strlen($subnetBin), "\x00");
|
||||||
if (($ipBin & $mask) === ($subnetBin & $mask)) return true;
|
|
||||||
|
// Tarkista suoraan
|
||||||
|
if (strlen($ipBin) === strlen($subnetBin) && ($ipBin & $mask) === ($subnetBin & $mask)) return true;
|
||||||
|
// Tarkista IPv4-equivalent
|
||||||
|
if ($ipv4Equivalent) {
|
||||||
|
$ipv4Bin = @inet_pton($ipv4Equivalent);
|
||||||
|
if ($ipv4Bin && strlen($ipv4Bin) === strlen($subnetBin) && ($ipv4Bin & $mask) === ($subnetBin & $mask)) return true;
|
||||||
|
}
|
||||||
|
// Tarkista IPv6-mapped
|
||||||
|
if ($ipv6MappedBin && strlen($ipv6MappedBin) === strlen($subnetBin) && ($ipv6MappedBin & $mask) === ($subnetBin & $mask)) return true;
|
||||||
} else {
|
} else {
|
||||||
// Yksittäinen IP (IPv4 tai IPv6)
|
// Yksittäinen IP (IPv4 tai IPv6)
|
||||||
$entryBin = @inet_pton($entry);
|
$entryBin = @inet_pton($entry);
|
||||||
if ($entryBin !== false && $ipBin === $entryBin) return true;
|
if ($entryBin === false) continue;
|
||||||
|
if ($ipBin === $entryBin) return true;
|
||||||
|
// Tarkista IPv4-equivalent
|
||||||
|
if ($ipv4Equivalent) {
|
||||||
|
$ipv4Bin = @inet_pton($ipv4Equivalent);
|
||||||
|
if ($ipv4Bin && $ipv4Bin === $entryBin) return true;
|
||||||
|
}
|
||||||
|
// Tarkista IPv6-mapped
|
||||||
|
if ($ipv6MappedBin && $ipv6MappedBin === $entryBin) return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user