is('api/external*')) { return $next($request); } // Only block in production environment if (app()->environment('production')) { $userAgent = strtolower($request->header('User-Agent', '')); $clientIp = $request->ip(); // Check if request has valid API key - bypass blocking for external systems if ($this->hasValidApiKey($request)) { return $next($request); } // Check if IP is in allowed list $allowedIps = config('api_security.allowed_ips', []); if (!empty($allowedIps) && in_array($clientIp, $allowedIps)) { return $next($request); } // Check if User-Agent is in allowed list $allowedUserAgents = config('api_security.allowed_user_agents', []); foreach ($allowedUserAgents as $allowedAgent) { if (str_contains($userAgent, strtolower($allowedAgent))) { return $next($request); } } // Check if the User-Agent matches any blocked patterns $blockedUserAgents = config('api_security.blocked_user_agents', []); foreach ($blockedUserAgents as $blockedAgent) { if (str_contains($userAgent, $blockedAgent)) { return response()->json([ 'success' => false, 'message' => config('api_security.blocked_message', 'API access is restricted in production environment. Please use the web interface.'), 'error' => 'API_TOOLS_BLOCKED' ], 403); } } // Additional check for requests without proper browser User-Agent // This catches tools that might not be in our list but don't look like browsers if ($this->isSuspiciousUserAgent($userAgent)) { return response()->json([ 'success' => false, 'message' => config('api_security.blocked_message', 'API access is restricted in production environment. Please use the web interface.'), 'error' => 'API_TOOLS_BLOCKED' ], 403); } } return $next($request); } /** * Check if the User-Agent looks suspicious (not a real browser) */ private function isSuspiciousUserAgent(string $userAgent): bool { $minLength = config('api_security.min_user_agent_length', 10); // If User-Agent is empty or very short, it's suspicious if (empty($userAgent) || strlen($userAgent) < $minLength) { return true; } // Check for common browser patterns $browserPatterns = [ 'mozilla', 'chrome', 'safari', 'firefox', 'edge', 'opera', 'webkit', 'gecko', 'trident', 'msie', ]; $hasBrowserPattern = false; foreach ($browserPatterns as $pattern) { if (str_contains($userAgent, $pattern)) { $hasBrowserPattern = true; break; } } // If no browser pattern is found, it's likely an API tool return !$hasBrowserPattern; } /** * Check if the request has a valid API key for external system access */ private function hasValidApiKey(Request $request): bool { // Check for API key in headers (X-API-Key, Authorization Bearer, or API-Key) $apiKey = $request->header('X-API-Key') ?? $request->header('API-Key') ?? $this->extractBearerToken($request->header('Authorization')); if (!$apiKey) { return false; } // Get valid API keys from configuration $validApiKeys = config('api_security.valid_api_keys', []); // If no API keys configured, return false if (empty($validApiKeys)) { return false; } // Check if the provided API key is valid return in_array($apiKey, $validApiKeys); } /** * Extract Bearer token from Authorization header */ private function extractBearerToken(?string $authorization): ?string { if (!$authorization) { return null; } if (preg_match('/Bearer\s+(.*)$/i', $authorization, $matches)) { return $matches[1]; } return null; } }