environment('local')) { return $next($request); } // 從環境變數讀取允許的 IP 列表 $allowedIps = $this->getAllowedIps(); // 如果沒有設定允許的 IP,記錄警告但允許通過(避免鎖死) if (empty($allowedIps)) { \Log::warning('ADMIN_ALLOWED_IPS not configured. Admin panel is accessible from any IP!'); return $next($request); } // 取得客戶端 IP(考慮反向代理) $clientIp = $this->getClientIp($request); // 檢查 IP 是否在允許列表中 if (!$this->isIpAllowed($clientIp, $allowedIps)) { \Log::warning('Unauthorized admin access attempt', [ 'ip' => $clientIp, 'url' => $request->fullUrl(), 'user_agent' => $request->userAgent(), ]); abort(403, 'Access denied. Your IP address is not authorized to access this area.'); } return $next($request); } /** * 從環境變數獲取允許的 IP 列表 * * @return array */ protected function getAllowedIps(): array { $ips = env('ADMIN_ALLOWED_IPS', ''); if (empty($ips)) { return []; } // 支援逗號分隔的 IP 列表 $ipList = array_map('trim', explode(',', $ips)); // 過濾掉空值 return array_filter($ipList); } /** * 取得客戶端真實 IP * * 考慮反向代理(Nginx, Caddy, CloudFlare 等)的情況 * * @param \Illuminate\Http\Request $request * @return string */ protected function getClientIp(Request $request): string { // 優先順序: // 1. X-Forwarded-For (反向代理) // 2. X-Real-IP (Nginx) // 3. CF-Connecting-IP (CloudFlare) // 4. REMOTE_ADDR (直接連接) $headers = [ 'HTTP_CF_CONNECTING_IP', // CloudFlare 'HTTP_X_REAL_IP', // Nginx 'HTTP_X_FORWARDED_FOR', // 標準反向代理 ]; foreach ($headers as $header) { if ($ip = $request->server($header)) { // X-Forwarded-For 可能包含多個 IP(逗號分隔),取第一個 if (str_contains($ip, ',')) { $ip = trim(explode(',', $ip)[0]); } // 驗證 IP 格式 if (filter_var($ip, FILTER_VALIDATE_IP)) { return $ip; } } } // 回退到 REMOTE_ADDR return $request->ip(); } /** * 檢查 IP 是否在允許列表中 * * @param string $clientIp * @param array $allowedIps * @return bool */ protected function isIpAllowed(string $clientIp, array $allowedIps): bool { foreach ($allowedIps as $allowedIp) { // 使用 Symfony 的 IpUtils 檢查(支援 CIDR 和範圍) if (IpUtils::checkIp($clientIp, $allowedIp)) { return true; } } return false; } }