fix(security): eliminate remaining critical vulnerabilities

- SystemFixService: removed ALL shell_exec/sudo calls (30+ instances), replaced with
  safe PHP alternatives (mkdir, chmod, disk_total_space, Artisan calls)
- InstallationController: added ALLOWED_SETTINGS whitelist to prevent arbitrary
  settings manipulation via request data
- ExceptionHandler: removed dangerous npm run build execution and hardcoded
  chown/chmod paths from auto-recovery
- AuthController: fixed user enumeration timing attack by running Hash::make()
  even when user doesn't exist (constant-time comparison)
- DDoSDetectionCommand: added IP validation (FILTER_VALIDATE_IP) before blocking
  to prevent iptables manipulation with spoofed/malicious IPs
- trackRequest: now validates IP before storing in cache
This commit is contained in:
root
2026-05-19 19:46:38 +02:00
parent 7f59024bef
commit b1739cabbf
6 changed files with 94 additions and 514 deletions
+25 -31
View File
@@ -53,41 +53,12 @@ class DDoSDetectionCommand extends Command
$data = Cache::get(self::CACHE_KEY_DDOS_TRACKING, []);
if (empty($data)) {
$data = $this->fetchApacheTrafficData();
$data = $this->getManualTrackingData();
}
return $data;
}
private function fetchApacheTrafficData(): array
{
$this->warn('Gebruik handmatige IP tracking (geen server logs toegang).');
return $this->getManualTrackingData();
}
private function tailFile(string $path, int $lines = 100): array
{
if (! file_exists($path)) {
return [];
}
$file = new \SplFileObject($path, 'r');
$file->seek(PHP_INT_MAX);
$totalLines = $file->key() + 1;
$startLine = max(0, $totalLines - $lines);
$result = [];
$file->seek($startLine);
while (! $file->eof()) {
$result[] = rtrim($file->current(), "\r\n");
$file->next();
}
return $result;
}
private function getManualTrackingData(): array
{
$tracking = Cache::get('manual_ip_tracking', []);
@@ -141,6 +112,10 @@ class DDoSDetectionCommand extends Command
$newBlocks = [];
foreach ($suspiciousIps as $ip => $details) {
if (! $this->isValidIp($ip)) {
continue;
}
$this->error("VERDACHTE IP GEDETECTEERD: {$ip}");
$this->table(
['Metric', 'Value'],
@@ -177,8 +152,19 @@ class DDoSDetectionCommand extends Command
Cache::put(self::CACHE_KEY_BLOCKED_IPS, $blockedIps, 3600);
}
private function isValidIp(string $ip): bool
{
return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false;
}
private function blockIp(string $ip): void
{
if (! $this->isValidIp($ip)) {
Log::warning("Attempted to block invalid IP: {$ip}");
return;
}
try {
$escapedIp = escapeshellarg($ip);
exec("iptables -A INPUT -s {$escapedIp} -j DROP 2>/dev/null");
@@ -192,6 +178,10 @@ class DDoSDetectionCommand extends Command
public static function trackRequest(string $ip): void
{
if (! filter_var($ip, FILTER_VALIDATE_IP)) {
return;
}
$tracking = Cache::get('manual_ip_tracking', []);
if (! isset($tracking[$ip])) {
@@ -201,7 +191,7 @@ class DDoSDetectionCommand extends Command
$tracking[$ip]['requests'][] = time();
$tracking[$ip]['count']++;
Cache::put('manual_ip_tracking', $tracking, self::TRACKING_DURATION_SECONDS);
Cache::put('manual_ip_tracking', $tracking, 300);
}
public static function getBlockedIps(): array
@@ -211,6 +201,10 @@ class DDoSDetectionCommand extends Command
public static function clearBlockedIp(string $ip): void
{
if (! filter_var($ip, FILTER_VALIDATE_IP)) {
return;
}
$blocked = Cache::get(self::CACHE_KEY_BLOCKED_IPS, []);
$blocked = array_filter($blocked, fn ($blockedIp) => $blockedIp !== $ip);
Cache::put(self::CACHE_KEY_BLOCKED_IPS, array_values($blocked), 3600);