Files
karczma-aplikacja-stoliki/api/request_ip.php
T

142 lines
3.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Czy REMOTE_ADDR to lokalny hop / reverse proxy, któremu wolno
* przekazać prawdziwy IP klienta w X-Forwarded-For.
*/
function isTrustedForwardingHop(string $remoteAddr): bool
{
$ip = normalizeClientIp($remoteAddr);
if ($ip === '') {
return false;
}
if (in_array($ip, ['127.0.0.1', '::1'], true)) {
return true;
}
// Proxy / Apache na LAN restauracji (goście Wi‑Fi widziani jako 10.x w XFF)
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
foreach (['10.0.0.0/8', '192.168.0.0/16', '172.16.0.0/12'] as $cidr) {
if (ipv4InCidr($ip, $cidr)) {
return true;
}
}
}
return false;
}
/**
* Adres IP klienta.
* X-Forwarded-For uznajemy wyłącznie, gdy połączenie przychodzi z zaufanego
* hopa (localhost / LAN) — inaczej każdy w internecie mógłby podrobić IP
* i ominąć geo. Bezpośredni dostęp z internetu = samo REMOTE_ADDR.
*/
function getRequestClientIp(): string
{
$remote = trim((string) ($_SERVER['REMOTE_ADDR'] ?? ''));
if (isTrustedForwardingHop($remote) && !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$parts = explode(',', (string) $_SERVER['HTTP_X_FORWARDED_FOR']);
$forwarded = normalizeClientIp(trim($parts[0]));
if ($forwarded !== '' && filter_var($forwarded, FILTER_VALIDATE_IP)) {
return $forwarded;
}
}
return normalizeClientIp($remote);
}
/**
* Hostnamey, dla których frontend pomija geolokalizację (dev / znany host).
* Wstrzykiwane do window.APP_CONFIG przez app.php.
*/
function getGeoBypassTrustedHosts(): array
{
return [
'82.160.190.247',
'localhost',
'127.0.0.1',
];
}
/**
* Pojedyncze IP z pominięciem geo (zewnętrzne, dev, przykładowe hosty LAN).
*/
function getGeoBypassTrustedIps(): array
{
return [
'82.160.190.247',
'127.0.0.1',
'::1',
'192.168.20.84',
'10.0.0.3',
'10.0.0.7',
];
}
/**
* Pule wewnętrznych sieci — goście na Wi‑Fi restauracji.
*/
function getGeoBypassTrustedCidrs(): array
{
return [
'10.0.0.0/24',
];
}
function normalizeClientIp(string $ip): string
{
if (strpos($ip, '::ffff:') === 0) {
return substr($ip, 7);
}
return $ip;
}
function ipv4InCidr(string $ip, string $cidr): bool
{
if (!str_contains($cidr, '/')) {
return false;
}
[$subnet, $bits] = explode('/', $cidr, 2);
$bits = (int) $bits;
if ($bits < 0 || $bits > 32) {
return false;
}
$ipLong = ip2long($ip);
$subnetLong = ip2long($subnet);
if ($ipLong === false || $subnetLong === false) {
return false;
}
$mask = $bits === 0 ? 0 : (-1 << (32 - $bits)) & 0xFFFFFFFF;
return ($ipLong & $mask) === ($subnetLong & $mask);
}
function isGeoBypassTrustedIp(string $ip): bool
{
$ip = normalizeClientIp($ip);
if ($ip === '') {
return false;
}
if (in_array($ip, getGeoBypassTrustedIps(), true)) {
return true;
}
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
foreach (getGeoBypassTrustedCidrs() as $cidr) {
if (ipv4InCidr($ip, $cidr)) {
return true;
}
}
}
return false;
}