false, 'message' => ''];
switch ($_POST['action']) {
case 'block_ip': $ip = escapeshellarg($_POST['ip'] ?? ''); if ($ip) { exec("fail2ban-client set sshd banip " . trim($_POST['ip'], "'") . " 2>&1", $output, $code); $result = ['success' => $code === 0, 'message' => $code === 0 ? "IP bloquée" : "Échec"]; } break;
case 'kill_query': case 'kill_connection': $pid = intval($_POST['pid'] ?? 0); if ($pid > 0) { try { $pdo = new PDO("pgsql:host=127.0.0.1;port=5432;dbname=adx_system", 'admin', 'admin123'); $stmt = $pdo->prepare("SELECT pg_terminate_backend(?)"); $stmt->execute([$pid]); $result = ['success' => true, 'message' => "Connexion #{$pid} terminée"]; } catch (Exception $e) { $result = ['success' => false, 'message' => $e->getMessage()]; } } break;
case 'block_db_ip': $ip = escapeshellarg($_POST['ip'] ?? ''); if ($ip) { exec("iptables -A INPUT -s {$ip} -p tcp --dport 5432 -j DROP 2>&1", $output, $code); $result = ['success' => $code === 0, 'message' => $code === 0 ? "IP bloquée de la DB" : "Échec"]; } break;
case 'vacuum_table': $table = preg_replace('/[^a-zA-Z0-9_]/', '', $_POST['table'] ?? ''); if ($table) { try { $pdo = new PDO("pgsql:host=127.0.0.1;port=5432;dbname=adx_system", 'admin', 'admin123'); $pdo->exec("VACUUM ANALYZE {$table}"); $result = ['success' => true, 'message' => "VACUUM {$table} OK"]; } catch (Exception $e) { $result = ['success' => false, 'message' => $e->getMessage()]; } } break;
case 'vacuum_full': $table = preg_replace('/[^a-zA-Z0-9_]/', '', $_POST['table'] ?? ''); if ($table) { exec("sudo -u postgres psql -d adx_system -c \"VACUUM FULL {$table}\" 2>&1", $output, $code); $result = ['success' => $code === 0, 'message' => $code === 0 ? "VACUUM FULL {$table} OK" : implode("\n", $output)]; } break;
case 'analyze_table': $table = preg_replace('/[^a-zA-Z0-9_]/', '', $_POST['table'] ?? ''); if ($table) { try { $pdo = new PDO("pgsql:host=127.0.0.1;port=5432;dbname=adx_system", 'admin', 'admin123'); $pdo->exec("ANALYZE {$table}"); $result = ['success' => true, 'message' => "ANALYZE {$table} OK"]; } catch (Exception $e) { $result = ['success' => false, 'message' => $e->getMessage()]; } } break;
case 'reindex_table': $table = preg_replace('/[^a-zA-Z0-9_]/', '', $_POST['table'] ?? ''); if ($table) { exec("sudo -u postgres psql -d adx_system -c \"REINDEX TABLE {$table}\" 2>&1", $output, $code); $result = ['success' => $code === 0, 'message' => $code === 0 ? "REINDEX {$table} OK" : implode("\n", $output)]; } break;
case 'cancel_query': $pid = intval($_POST['pid'] ?? 0); if ($pid > 0) { try { $pdo = new PDO("pgsql:host=127.0.0.1;port=5432;dbname=adx_system", 'admin', 'admin123'); $stmt = $pdo->prepare("SELECT pg_cancel_backend(?)"); $stmt->execute([$pid]); $result = ['success' => true, 'message' => "Requête #{$pid} annulée"]; } catch (Exception $e) { $result = ['success' => false, 'message' => $e->getMessage()]; } } break;
case 'clear_idle_connections': try { $pdo = new PDO("pgsql:host=127.0.0.1;port=5432;dbname=adx_system", 'admin', 'admin123'); $stmt = $pdo->query("SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle' AND pid <> pg_backend_pid() AND query_start < NOW() - INTERVAL '10 minutes'"); $count = $stmt->rowCount(); $result = ['success' => true, 'message' => "{$count} connexion(s) fermée(s)"]; } catch (Exception $e) { $result = ['success' => false, 'message' => $e->getMessage()]; } break;
case 'reset_stats': try { $pdo = new PDO("pgsql:host=127.0.0.1;port=5432;dbname=adx_system", 'admin', 'admin123'); $pdo->exec("SELECT pg_stat_reset()"); $result = ['success' => true, 'message' => "Stats remises à zéro"]; } catch (Exception $e) { $result = ['success' => false, 'message' => $e->getMessage()]; } break;
case 'ignore_ip': $ip = $_POST['ip'] ?? ''; if ($ip && !in_array($ip, $_SESSION['ignored_ips'])) { $_SESSION['ignored_ips'][] = $ip; $result = ['success' => true, 'message' => "IP {$ip} ignorée"]; } break;
case 'whitelist_ip': $ip = $_POST['ip'] ?? ''; if ($ip && filter_var($ip, FILTER_VALIDATE_IP)) { @mkdir('/opt/wevads/config', 0755, true); $current = file_exists($whitelist_file) ? file_get_contents($whitelist_file) : ''; if (strpos($current, $ip) === false) { file_put_contents($whitelist_file, $ip . "\n", FILE_APPEND); $result = ['success' => true, 'message' => "IP {$ip} autorisée"]; } else { $result = ['success' => true, 'message' => "IP déjà autorisée"]; } } break;
case 'clear_ignored': $_SESSION['ignored_ips'] = []; $result = ['success' => true, 'message' => 'IPs ignorées effacées']; break;
}
echo json_encode($result); exit;
}
$effective_allowed_ips = $allowed_db_ips;
function getDatabaseStats() {
global $effective_allowed_ips, $allowed_db_users, $sensitive_tables;
$ignored_ips = $_SESSION['ignored_ips'] ?? [];
$stats = ['connected' => false, 'connections' => ['active' => 0, 'idle' => 0, 'idle_in_transaction' => 0, 'max' => 100, 'total' => 0], 'cache_hit_ratio' => 0, 'databases' => [], 'tables' => [], 'slow_queries' => [], 'active_queries' => [], 'locks' => 0, 'waiting_locks' => [], 'uptime' => 'N/A', 'transactions' => 0, 'intrusion_alerts' => [], 'recent_connections' => [], 'long_running' => [], 'idle_connections' => 0];
try {
$pdo = new PDO("pgsql:host=127.0.0.1;port=5432;dbname=adx_system", 'admin', 'admin123', [PDO::ATTR_TIMEOUT => 5]);
$stats['connected'] = true;
// Intrusion alerts
$stmt = $pdo->query("SELECT pid, usename, client_addr, datname, state, query FROM pg_stat_activity WHERE client_addr IS NOT NULL AND client_addr::text NOT IN ('127.0.0.1', '::1')");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $ip = $row['client_addr']; if (!in_array($ip, $effective_allowed_ips) && !in_array($ip, $ignored_ips)) { $stats['intrusion_alerts'][] = ['type' => 'EXTERNAL_CONNECTION', 'severity' => 'CRITICAL', 'title' => 'Connexion externe', 'description' => "IP {$ip} connectée comme '{$row['usename']}'", 'pid' => $row['pid'], 'source_ip' => $ip]; } }
// Connections
$stmt = $pdo->query("SELECT pid, usename, client_addr, datname, application_name, backend_start, state, query, EXTRACT(EPOCH FROM (NOW() - backend_start))::int as duration FROM pg_stat_activity WHERE backend_type = 'client backend' ORDER BY backend_start DESC LIMIT 25");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $row['is_trusted'] = in_array($row['client_addr'], $effective_allowed_ips) || in_array($row['client_addr'], $ignored_ips) || $row['client_addr'] === null; $row['is_ignored'] = in_array($row['client_addr'], $ignored_ips); $stats['recent_connections'][] = $row; }
$stmt = $pdo->query("SELECT state, count(*) as cnt FROM pg_stat_activity GROUP BY state");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { if ($row['state'] === 'active') $stats['connections']['active'] = $row['cnt']; elseif ($row['state'] === 'idle') $stats['connections']['idle'] = $row['cnt']; elseif ($row['state'] === 'idle in transaction') $stats['connections']['idle_in_transaction'] = $row['cnt']; }
$stmt = $pdo->query("SELECT count(*) as cnt FROM pg_stat_activity WHERE state = 'idle' AND query_start < NOW() - INTERVAL '10 minutes'"); $stats['idle_connections'] = $stmt->fetch(PDO::FETCH_ASSOC)['cnt'] ?? 0;
$stmt = $pdo->query("SELECT count(*) as total FROM pg_stat_activity"); $stats['connections']['total'] = $stmt->fetch(PDO::FETCH_ASSOC)['total'];
$stmt = $pdo->query("SHOW max_connections"); $stats['connections']['max'] = $stmt->fetch(PDO::FETCH_ASSOC)['max_connections'] ?? 100;
$stmt = $pdo->query("SELECT ROUND(sum(blks_hit) * 100.0 / NULLIF(sum(blks_hit) + sum(blks_read), 0), 2) as ratio FROM pg_stat_database"); $stats['cache_hit_ratio'] = $stmt->fetch(PDO::FETCH_ASSOC)['ratio'] ?? 0;
$stmt = $pdo->query("SELECT datname, pg_size_pretty(pg_database_size(datname)) as size FROM pg_database WHERE datname NOT LIKE 'template%' ORDER BY pg_database_size(datname) DESC LIMIT 5"); $stats['databases'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt = $pdo->query("SELECT relname as name, n_live_tup as rows, n_dead_tup as dead, pg_size_pretty(pg_total_relation_size(relid)) as size, last_vacuum, last_autovacuum, last_analyze FROM pg_stat_user_tables ORDER BY n_live_tup DESC LIMIT 15"); $stats['tables'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt = $pdo->query("SELECT pid, usename, client_addr, datname, state, EXTRACT(EPOCH FROM (NOW() - query_start))::int as duration, LEFT(query, 100) as query FROM pg_stat_activity WHERE state = 'active' AND query NOT LIKE '%pg_stat_activity%' ORDER BY query_start LIMIT 15"); $stats['active_queries'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt = $pdo->query("SELECT pid, usename, client_addr, datname, EXTRACT(EPOCH FROM (NOW() - query_start))::int as duration, LEFT(query, 150) as query FROM pg_stat_activity WHERE state = 'active' AND NOW() - query_start > interval '5 seconds' AND query NOT LIKE '%pg_stat_activity%' ORDER BY duration DESC LIMIT 10"); $stats['slow_queries'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt = $pdo->query("SELECT pid, usename, client_addr, datname, state, EXTRACT(EPOCH FROM (NOW() - query_start))::int as duration, LEFT(query, 150) as query FROM pg_stat_activity WHERE NOW() - query_start > interval '1 minute' AND query NOT LIKE '%pg_stat_activity%' AND state != 'idle' ORDER BY duration DESC LIMIT 5"); $stats['long_running'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt = $pdo->query("SELECT bl.pid as blocked_pid, a.usename as blocked_user, kl.pid as blocking_pid, ka.usename as blocking_user, a.query as blocked_query FROM pg_catalog.pg_locks bl JOIN pg_catalog.pg_stat_activity a ON a.pid = bl.pid JOIN pg_catalog.pg_locks kl ON kl.transactionid = bl.transactionid AND kl.pid != bl.pid JOIN pg_catalog.pg_stat_activity ka ON ka.pid = kl.pid WHERE NOT bl.granted LIMIT 10"); $stats['waiting_locks'] = $stmt->fetchAll(PDO::FETCH_ASSOC); $stats['locks'] = count($stats['waiting_locks']);
$stmt = $pdo->query("SELECT NOW() - pg_postmaster_start_time() as uptime"); $stats['uptime'] = $stmt->fetch(PDO::FETCH_ASSOC)['uptime'] ?? 'N/A';
$stmt = $pdo->query("SELECT sum(xact_commit + xact_rollback) as total FROM pg_stat_database"); $stats['transactions'] = $stmt->fetch(PDO::FETCH_ASSOC)['total'] ?? 0;
} catch (Exception $e) { $stats['error'] = $e->getMessage(); }
return $stats;
}
function getSecurityReport() {
global $effective_allowed_ips;
$ignored_ips = $_SESSION['ignored_ips'] ?? [];
$report = ['status' => 'secure', 'score' => 100, 'checks' => [], 'threats' => [], 'timestamp' => date('Y-m-d H:i:s')];
$known_ips = $GLOBALS["allowed_db_ips"];
try {
$pdo = new PDO("pgsql:host=127.0.0.1;port=5432;dbname=adx_system", 'admin', 'admin123', [PDO::ATTR_TIMEOUT => 5]);
$stmt = $pdo->query("SELECT pid, usename, client_addr, query FROM pg_stat_activity WHERE state != 'idle' AND query NOT LIKE '%pg_stat_activity%'"); $suspicious = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { if (preg_match('/(DROP|DELETE FROM|TRUNCATE|pg_dump|COPY.*TO)/i', $row['query'])) { $suspicious[] = $row; $report['threats'][] = ['type' => 'DATABASE', 'severity' => 'CRITICAL', 'title' => 'Requête suspecte', 'description' => substr($row['query'], 0, 100), 'source' => $row['client_addr'] ?: 'local', 'pid' => $row['pid']]; } }
if (count($suspicious) > 0) { $report['status'] = 'critical'; $report['score'] -= 40; }
$report['checks'][] = ['name' => 'Requêtes DB', 'status' => count($suspicious) == 0 ? 'ok' : 'failed', 'detail' => count($suspicious) == 0 ? 'OK' : count($suspicious) . ' suspecte(s)'];
} catch (Exception $e) { $report['checks'][] = ['name' => 'Requêtes DB', 'status' => 'warning', 'detail' => 'Non vérifiable']; }
$ssh_output = shell_exec("who 2>/dev/null"); $unknown_ssh = [];
if ($ssh_output) { preg_match_all('/\(([0-9.]+)\)/', $ssh_output, $matches); foreach ($matches[1] as $ip) { if (!in_array($ip, $known_ips)) { $unknown_ssh[] = $ip; $report['threats'][] = ['type' => 'SSH', 'severity' => 'HIGH', 'title' => 'Connexion SSH inconnue', 'description' => $ip, 'source' => $ip]; } } }
if (count($unknown_ssh) > 0) { $report['score'] -= 20; if ($report['status'] === 'secure') $report['status'] = 'warning'; }
$report['checks'][] = ['name' => 'Sessions SSH', 'status' => count($unknown_ssh) == 0 ? 'ok' : 'warning', 'detail' => count($unknown_ssh) == 0 ? 'OK' : count($unknown_ssh) . ' inconnue(s)'];
$failed = shell_exec("grep 'Failed password' /var/log/auth.log 2>/dev/null | tail -100 | awk '{print \$(NF-3)}' | sort | uniq -c | sort -rn | head -5"); $brute = [];
if ($failed) { foreach (array_filter(explode("\n", trim($failed))) as $line) { if (preg_match('/^\s*(\d+)\s+(.+)$/', $line, $m) && intval($m[1]) >= 10 && !in_array($m[2], $ignored_ips)) { $brute[] = $m[2]; $report['threats'][] = ['type' => 'BRUTE_FORCE', 'severity' => 'HIGH', 'title' => 'Attaque force brute', 'description' => "{$m[1]} tentatives depuis {$m[2]}", 'source' => $m[2]]; } } }
if (count($brute) > 0) { $report['score'] -= 25; if ($report['status'] === 'secure') $report['status'] = 'warning'; }
$report['checks'][] = ['name' => 'Force brute', 'status' => count($brute) == 0 ? 'ok' : 'warning', 'detail' => count($brute) == 0 ? 'OK' : count($brute) . ' attaque(s)'];
$malware = shell_exec("ps aux | grep -E '(cryptominer|xmrig|minerd)' | grep -v grep");
if (!empty(trim($malware ?? ''))) { $report['status'] = 'critical'; $report['score'] -= 50; }
$report['checks'][] = ['name' => 'Malware', 'status' => empty(trim($malware ?? '')) ? 'ok' : 'failed', 'detail' => empty(trim($malware ?? '')) ? 'Aucun' : 'Détecté!'];
$disk = intval(shell_exec("df -h / | tail -1 | awk '{print \$5}' | tr -d '%'")); if ($disk > 90) $report['score'] -= 15;
$report['checks'][] = ['name' => 'Disque', 'status' => $disk <= 90 ? 'ok' : 'warning', 'detail' => "{$disk}%"];
$fw = intval(shell_exec("iptables -L -n 2>/dev/null | wc -l"));
$report['checks'][] = ['name' => 'Pare-feu', 'status' => $fw > 10 ? 'ok' : 'warning', 'detail' => $fw > 10 ? 'Actif' : 'Limité'];
$f2b = trim(shell_exec("systemctl is-active fail2ban 2>/dev/null")) === 'active';
$report['checks'][] = ['name' => 'Fail2Ban', 'status' => $f2b ? 'ok' : 'warning', 'detail' => $f2b ? 'Actif' : 'Inactif'];
if (!$f2b) $report['score'] -= 5;
if ($report['score'] < 50) $report['status'] = 'critical'; elseif ($report['score'] < 80) $report['status'] = 'warning';
$report['score'] = max(0, $report['score']);
return $report;
}
$report = getSecurityReport();
$db_stats = getDatabaseStats();
if (!empty($db_stats['intrusion_alerts'])) { $critical_db = count(array_filter($db_stats['intrusion_alerts'], fn($a) => $a['severity'] === 'CRITICAL')); if ($critical_db > 0) { $report['status'] = 'critical'; $report['score'] = max(0, $report['score'] - ($critical_db * 20)); } }
$is_current_ip_whitelisted = in_array($current_ip, $effective_allowed_ips);
$is_current_ip_ignored = false;
?>
🛡️ Sécurité WEVAL
Cette page surveille la sécurité de votre serveur et base de données. Score 100 = parfait. Actualisation auto: 30 sec.
Votre IP
= htmlspecialchars($current_ip) ?>
= $is_current_ip_whitelisted ? '✓ Autorisée' : ($is_current_ip_ignored ? 'Ignorée' : 'Inconnue') ?>
0): ?>
= count($_SESSION['ignored_ips']) ?> IP(s) ignorée(s): = implode(', ', $_SESSION['ignored_ips']) ?>
= $report['score'] ?>
/ 100
Sécurisé Attention Critique
Vérifications
= $check['name'] ?>
= $check['detail'] ?>
⚠️
Connexions suspectes
Des IPs inconnues accèdent à la DB. Si c'est vous (VPN), autorisez. Sinon, bloquez.
= htmlspecialchars($alert['description']) ?>
= htmlspecialchars($threat['description']) ?>
= $db_stats['connected'] ? '✅ Connecté' : '❌ Déconnecté' ?>Uptime: = explode('.', $db_stats['uptime'])[0] ?>
Actions rapides
5): ?>
⚠️
Trop de connexions inactives
= $db_stats['idle_connections'] ?> connexions dorment depuis +10 min.
🔒
Blocage détecté
= count($db_stats['waiting_locks']) ?> requête(s) bloquée(s).
🐢
Requêtes longues (> 1 min)
= count($db_stats['long_running']) ?> requête(s) trop lente(s).
| PID | User | IP | Base | État | Durée | Actions |
|---|
| = $conn['pid'] ?> | = $conn['usename'] ?> | = $conn['client_addr'] ?? 'local' ?> = $is_my ? 'VOUS' : '' ?> | = $conn['datname'] ?> | = $conn['state'] === 'active' ? '● Actif' : '○ Inactif' ?> | = floor($conn['duration']/60) ?>m | |
80 ? 'bad' : ($conn_pct > 50 ? 'warn' : 'good'); ?>
Actives= $db_stats['connections']['active'] ?>
Inactives= $db_stats['connections']['idle'] ?>
Total / Max= $db_stats['connections']['total'] ?> / = $db_stats['connections']['max'] ?>
= 95 ? 'good' : ($db_stats['cache_hit_ratio'] >= 90 ? 'warn' : 'bad'); ?>
Cache Hit= $db_stats['cache_hit_ratio'] ?>%
Transactions= number_format($db_stats['transactions']) ?>
Verrous= $db_stats['locks'] ?>
= $db['datname'] ?>= $db['size'] ?>
| PID | User | Durée | Requête | Actions |
|---|
| = $q['pid'] ?> | = $q['usename'] ?> | = $q['duration'] ?>s | = htmlspecialchars($q['query']) ?> | |
| PID | User | Base | Durée | Requête | Actions |
|---|
| = $q['pid'] ?> | = $q['usename'] ?> | = $q['datname'] ?> | = $q['duration'] ?>s | = htmlspecialchars($q['query']) ?> | |
Actions: 🧹 VACUUM | 📦 VACUUM FULL (⚠️ bloque!) | 📊 ANALYZE | 📑 REINDEX
| Table | Taille | Lignes | Dead | Dernier VACUUM | État | Actions |
10000; ?>| = $t['name'] ?> | = $t['size'] ?> | = number_format($t['rows']) ?> | = number_format($t['dead']) ?> | = $t['last_vacuum'] ? date('d/m H:i', strtotime($t['last_vacuum'])) : ($t['last_autovacuum'] ? date('d/m H:i', strtotime($t['last_autovacuum'])) . ' (auto)' : 'Jamais') ?> | = $needs_vacuum ? '⚠️ VACUUM' : '✓ OK' ?> | |
// ========== API CHECK IP SECURITY ==========
if (isset($_GET['action']) && $_GET['action'] === 'check_ip') {
header('Content-Type: application/json');
$currentIP = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? 'unknown';
if (strpos($currentIP, ',') !== false) {
$currentIP = trim(explode(',', $currentIP)[0]);
}
// IPs connues/whitelistées (ajoute tes IPs habituelles ici)
$knownIPs = [
'89.167.40.150', // Hetzner prod
'151.80.235.110', // OVH
'46.62.241.15', // Nouveau serveur
// Ajoute tes IPs personnelles ici