getRemoteIp(), (string) $request->header('x-forwarded-for', ''), (array) config('mini_api.trusted_proxies', []) ); } /** * @param list $trustedProxies peers allowed to speak for the caller */ public static function from(string $peer, string $forwardedFor, array $trustedProxies): string { $proxies = []; foreach ($trustedProxies as $entry) { $ip = self::normalize(trim((string) $entry)); if ($ip !== null) { $proxies[] = $ip; } } $peerIp = self::normalize($peer); if ($peerIp === null || !in_array($peerIp, $proxies, true)) { return $peer; } // Walk the chain right to left and take the first hop that is not another proxy. foreach (array_reverse(explode(',', $forwardedFor)) as $candidate) { $ip = self::normalize(trim($candidate)); if ($ip !== null && !in_array($ip, $proxies, true)) { return $ip; } } return $peer; } /** Accepts bare addresses plus the [::1]:port and 1.2.3.4:port forms proxies emit. */ private static function normalize(string $value): ?string { if ($value === '') { return null; } if (str_starts_with($value, '[')) { $end = strpos($value, ']'); $value = $end === false ? '' : substr($value, 1, $end - 1); } elseif (substr_count($value, ':') === 1) { $value = explode(':', $value, 2)[0]; } $packed = @inet_pton($value); return $packed === false ? null : (string) inet_ntop($packed); } }