first init
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class BlockApiToolsMiddleware
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
// Check if API tool blocking is enabled
|
||||
if (!config('api_security.block_api_tools_in_production')) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
// Skip blocking for external API routes - they use API key authentication
|
||||
if ($request->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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user