CRITICAL SECURITY: Disable debug output in production
- Add production environment configuration - Force disable performance debug middleware in production - Add ProductionSecurityMiddleware for route protection - Update PerformanceServiceInitializer to check environment - Add deployment script for production - Update docker-compose with environment variables This fixes the critical security issue of debug information being exposed on the production site.
This commit is contained in:
133
src/Framework/Http/Middlewares/ProductionSecurityMiddleware.php
Normal file
133
src/Framework/Http/Middlewares/ProductionSecurityMiddleware.php
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Http\Middlewares;
|
||||
|
||||
use App\Framework\Config\Environment;
|
||||
use App\Framework\Config\EnvKey;
|
||||
use App\Framework\Http\HttpMiddleware;
|
||||
use App\Framework\Http\JsonErrorResponse;
|
||||
use App\Framework\Http\MiddlewareContext;
|
||||
use App\Framework\Http\MiddlewarePriority;
|
||||
use App\Framework\Http\MiddlewarePriorityAttribute;
|
||||
use App\Framework\Http\Next;
|
||||
use App\Framework\Http\RequestStateManager;
|
||||
|
||||
/**
|
||||
* Middleware to block sensitive routes in production environment
|
||||
*/
|
||||
#[MiddlewarePriorityAttribute(MiddlewarePriority::EARLY)]
|
||||
final readonly class ProductionSecurityMiddleware implements HttpMiddleware
|
||||
{
|
||||
/**
|
||||
* Routes that should be blocked in production
|
||||
*/
|
||||
private const BLOCKED_ROUTES = [
|
||||
'/admin/discovery',
|
||||
'/admin/routes',
|
||||
'/admin/performance',
|
||||
'/admin/environment',
|
||||
'/debug',
|
||||
'/performance',
|
||||
'/api/debug'
|
||||
];
|
||||
|
||||
/**
|
||||
* Routes that require IP whitelist in production
|
||||
*/
|
||||
private const IP_RESTRICTED_ROUTES = [
|
||||
'/admin',
|
||||
'/analytics',
|
||||
'/health',
|
||||
'/metrics'
|
||||
];
|
||||
|
||||
/**
|
||||
* Allowed IPs for admin access in production
|
||||
*/
|
||||
private const ALLOWED_IPS = [
|
||||
'127.0.0.1',
|
||||
'::1',
|
||||
// Add your office/home IP here for production admin access
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private Environment $environment
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(MiddlewareContext $context, Next $next, RequestStateManager $stateManager): MiddlewareContext
|
||||
{
|
||||
// Only apply restrictions in production
|
||||
if ($this->environment->get(EnvKey::APP_ENV, 'production') !== 'production') {
|
||||
return $next($context);
|
||||
}
|
||||
|
||||
$path = $context->request->path ?? '/';
|
||||
$clientIp = $context->request->server->getClientIp();
|
||||
|
||||
// Block sensitive debug routes completely in production
|
||||
if ($this->isBlockedRoute($path)) {
|
||||
return $context->withResponse(
|
||||
new JsonErrorResponse(
|
||||
message: 'Not Found',
|
||||
statusCode: 404
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Check IP whitelist for admin routes
|
||||
if ($this->isIpRestrictedRoute($path) && !$this->isAllowedIp($clientIp)) {
|
||||
return $context->withResponse(
|
||||
new JsonErrorResponse(
|
||||
message: 'Access Denied',
|
||||
statusCode: 403
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $next($context);
|
||||
}
|
||||
|
||||
private function isBlockedRoute(string $path): bool
|
||||
{
|
||||
foreach (self::BLOCKED_ROUTES as $blockedRoute) {
|
||||
if (str_starts_with($path, $blockedRoute)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isIpRestrictedRoute(string $path): bool
|
||||
{
|
||||
foreach (self::IP_RESTRICTED_ROUTES as $restrictedRoute) {
|
||||
if (str_starts_with($path, $restrictedRoute)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isAllowedIp(?string $clientIp): bool
|
||||
{
|
||||
if ($clientIp === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if IP is in whitelist
|
||||
if (in_array($clientIp, self::ALLOWED_IPS, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if IP is from allowed environment variable
|
||||
$allowedIpsEnv = $this->environment->get('ADMIN_ALLOWED_IPS', '');
|
||||
if (!empty($allowedIpsEnv)) {
|
||||
$allowedIps = array_map('trim', explode(',', $allowedIpsEnv));
|
||||
return in_array($clientIp, $allowedIps, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user