Sicherheitsleitfaden
SLAED CMS wird mit Fokus auf Sicherheit entwickelt und beinhaltet ein mehrschichtiges Schutzsystem gegen wichtige Arten von Angriffen und Bedrohungen.
Inhaltsverzeichnis
Sicherheitsarchitektur
Sicherheitsprinzipien
- Defense in Depth - Mehrschichtiger Schutz
- Prinzip der geringsten Rechte - Minimal notwendige Rechte
- Eingabevalidierung - Validierung aller Eingabedaten
- Ausgabekodierung - Kodierung aller Ausgabedaten
- Standardmäßig sicher - Sichere Einstellungen standardmäßig
Hauptkomponenten des Schutzes
┌─────────────────────────────────────────┐
│ SICHERHEITSEBENEN │
├─────────────────────────────────────────┤
│ 1. Webserver-Sicherheit (Apache/Nginx) │
│ 2. Anwendungsfirewall │
│ 3. Eingabevalidierung & Bereinigung │
│ 4. Authentifizierung & Autorisierung │
│ 5. Sitzungsmanagement │
│ 6. CSRF-Schutz │
│ 7. XSS-Prävention │
│ 8. SQL-Injection-Prävention │
│ 9. Datei-Upload-Sicherheit │
│ 10. Protokollierung & Überwachung │
└─────────────────────────────────────────┘
Schutz vor Hauptangriffen
1. SQL-Injection-Schutz
Verwendung von Prepared Statements
// SICHER - Prepared Statements
$stmt = $db->prepare("SELECT * FROM {$prefix}_users WHERE user_id = ? AND active = ?");
$stmt->bind_param("ii", $user_id, $active);
$stmt->execute();
$result = $stmt->get_result();
// UNSICHER - Direkte Ersetzung (NICHT VERWENDEN!)
$query = "SELECT * FROM {$prefix}_users WHERE user_id = " . $user_id;
Automatische Filterung in Kernfunktionen
// getVar-Funktion filtert automatisch Eingabedaten
$id = getVar('get', 'id', 'num'); // Nur Zahlen
$email = getVar('post', 'email', 'email'); // E-Mail-Validierung
$text = getVar('post', 'text', 'text'); // Sicherer Text
2. XSS (Cross-Site Scripting) Schutz
Eingabefilterung
// Automatische Filterung in getVar
function getVar($method, $name, $type, $default = '') {
switch($type) {
case 'text':
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
case 'html':
return filter_html($value);
case 'var':
return preg_replace('#[^a-zA-Z0-9_-]#', '', $value);
}
}
// Zusätzlicher Schutz beim Ausgeben
function xss_clean($str) {
$str = htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
$str = preg_replace('#javascript:#i', '', $str);
$str = preg_replace('#vbscript:#i', '', $str);
$str = preg_replace('#onload#i', '', $str);
return $str;
}
Content Security Policy (CSP)
// In Vorlagendateien
header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://www.google.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;");
3. CSRF (Cross-Site Request Forgery) Schutz
Token-Erstellung und Verifizierung
// CSRF-Token erzeugen
function generate_csrf_token() {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
// CSRF-Token verifizieren
function verify_csrf_token($token) {
if (!isset($_SESSION['csrf_token'])) {
return false;
}
return hash_equals($_SESSION['csrf_token'], $token);
}
// Verwendung in Formularen
echo '<input type="hidden" name="csrf_token" value="'.generate_csrf_token().'">';
// Verifizierung bei der Formularverarbeitung
if (!verify_csrf_token(getVar('post', 'csrf_token', 'text'))) {
die('CSRF-Token stimmt nicht überein');
}
4. Datei-Upload-Sicherheit
Strenge Datei-Upload-Prüfung
function secure_file_upload($file, $allowed_types, $max_size, $upload_dir) {
// 1. Dateityp nach Erweiterung prüfen
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowed_types)) {
return false;
}
// 2. MIME-Typ prüfen
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
$allowed_mimes = array(
'jpg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'pdf' => 'application/pdf'
);
if (!isset($allowed_mimes[$ext]) || $allowed_mimes[$ext] !== $mime) {
return false;
}
// 3. Größe prüfen
if ($file['size'] > $max_size) {
return false;
}
// 4. Auf ausführbare Dateien prüfen
$dangerous_extensions = array('php', 'phtml', 'php3', 'php4', 'php5', 'pl', 'py', 'jsp', 'asp', 'sh', 'cgi');
if (in_array($ext, $dangerous_extensions)) {
return false;
}
// 5. Sicheren Dateinamen generieren
$safe_name = uniqid().'_'.preg_replace('#[^a-zA-Z0-9.-]#', '_', basename($file['name']));
// 6. Datei verschieben
if (move_uploaded_file($file['tmp_name'], $upload_dir.'/'.$safe_name)) {
return $safe_name;
}
return false;
}
Zusätzliche Sicherheitsmaßnahmen
# .htaccess im Uploads-Ordner
<Files "*.php">
Deny from all
</Files>
<Files "*.phtml">
Deny from all
</Files>
<Files "*.php3">
Deny from all
</Files>
Authentifizierung & Autorisierung
Rollen- und Zugriffssystem
// Benutzerhierarchie
define('USER_GUEST', 0); // Gast
define('USER_MEMBER', 1); // Benutzer
define('USER_MODERATOR', 2); // Moderator
define('USER_ADMIN', 3); // Administrator
define('USER_SUPERADMIN', 4); // Super-Administrator
// Zugriffsebene prüfen
function check_access_level($required_level, $user_level = null) {
global $user;
if ($user_level === null) {
$user_level = isset($user[3]) ? intval($user[3]) : USER_GUEST;
}
return $user_level >= $required_level;
}
// Modul-Admin prüfen
function is_module_admin($module) {
global $db, $prefix, $user;
if (!is_user()) return false;
if (is_admin()) return true;
$user_id = intval($user[0]);
$stmt = $db->prepare("SELECT COUNT(*) FROM {$prefix}_module_admins WHERE user_id = ? AND module = ?");
$stmt->bind_param("is", $user_id, $module);
$stmt->execute();
$result = $stmt->get_result();
$count = $result->fetch_row()[0];
return $count > 0;
}
Sichere Authentifizierung
// Passwort-Hashing
function hash_password($password) {
return password_hash($password, PASSWORD_ARGON2ID, [
'memory_cost' => 65536, // 64MB
'time_cost' => 4, // 4 Iterationen
'threads' => 3, // 3 Threads
]);
}
// Passwort-Verifizierung
function verify_password($password, $hash) {
return password_verify($password, $hash);
}
// Passwort-Stärke prüfen
function check_password_strength($password) {
$strength = 0;
if (strlen($password) >= 8) $strength++;
if (preg_match('/[a-z]/', $password)) $strength++;
if (preg_match('/[A-Z]/', $password)) $strength++;
if (preg_match('/[0-9]/', $password)) $strength++;
if (preg_match('/[^a-zA-Z0-9]/', $password)) $strength++;
return $strength; // 0-5
}
Sitzungsmanagement
// Sichere Sitzungseinstellungen
function init_secure_session() {
// Sitzungs-Cookie-Einstellungen
$cookie_params = array(
'lifetime' => 0,
'path' => '/',
'domain' => '',
'secure' => isset($_SERVER['HTTPS']),
'httponly' => true,
'samesite' => 'Strict'
);
session_set_cookie_params($cookie_params);
// Sitzungs-ID neu generieren
if (!isset($_SESSION['initiated'])) {
session_regenerate_id(true);
$_SESSION['initiated'] = true;
}
// IP-Adresse und User-Agent prüfen
if (isset($_SESSION['user_ip']) && $_SESSION['user_ip'] !== getIp()) {
session_destroy();
return false;
}
if (isset($_SESSION['user_agent']) && $_SESSION['user_agent'] !== getUserAgent()) {
session_destroy();
return false;
}
$_SESSION['user_ip'] = getIp();
$_SESSION['user_agent'] = getUserAgent();
return true;
}
// Sichere Sitzungsbeendigung
function secure_logout() {
$_SESSION = array();
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
session_destroy();
}
Schutz vor automatisierten Angriffen
Rate Limiting / Flood-Schutz
// Flood-Schutz
function check_flood_protection($action, $ip = null, $user_id = null) {
global $db, $prefix, $confs;
if (!$confs['flood_enable']) return true;
$ip = $ip ?: getIp();
$user_id = $user_id ?: (is_user() ? intval($user[0]) : 0);
$time_limit = time() - intval($confs['flood_time']);
// Versuche zählen
$stmt = $db->prepare("SELECT COUNT(*) FROM {$prefix}_flood_log WHERE action = ? AND (ip = ? OR user_id = ?) AND timestamp > ?");
$stmt->bind_param("ssii", $action, $ip, $user_id, $time_limit);
$stmt->execute();
$attempts = $stmt->get_result()->fetch_row()[0];
if ($attempts >= intval($confs['flood_attempts'])) {
// Versuch protokollieren
log_security_event('FLOOD_DETECTED', array(
'action' => $action,
'ip' => $ip,
'user_id' => $user_id,
'attempts' => $attempts
));
return false;
}
// Versuch aufzeichnen
$stmt = $db->prepare("INSERT INTO {$prefix}_flood_log (action, ip, user_id, timestamp) VALUES (?, ?, ?, ?)");
$current_time = time();
$stmt->bind_param("ssii", $action, $ip, $user_id, $current_time);
$stmt->execute();
return true;
}
CAPTCHA-Integration
// Google reCAPTCHA v3
function verify_recaptcha_v3($response, $action = 'homepage') {
global $confs;
if (!$confs['captcha_enable']) return true;
$secret_key = $confs['recaptcha_secret_key'];
$verify_url = 'https://www.google.com/recaptcha/api/siteverify';
$data = array(
'secret' => $secret_key,
'response' => $response,
'remoteip' => getIp()
);
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($data)
)
);
$context = stream_context_create($options);
$result = file_get_contents($verify_url, false, $context);
$response_data = json_decode($result, true);
if ($response_data['success'] &&
$response_data['action'] === $action &&
$response_data['score'] >= floatval($confs['recaptcha_score'])) {
return true;
}
log_security_event('CAPTCHA_FAILED', array(
'ip' => getIp(),
'score' => $response_data['score'] ?? 0,
'action' => $action
));
return false;
}
Sicherheitsüberwachung & Protokollierung
Protokollierungssystem
// Sicherheitsereignisse protokollieren
function log_security_event($event_type, $data = array()) {
global $user;
$log_entry = array(
'timestamp' => date('Y-m-d H:i:s'),
'event_type' => $event_type,
'ip' => getIp(),
'user_agent' => getUserAgent(),
'user_id' => is_user() ? intval($user[0]) : 0,
'data' => $data
);
$log_line = json_encode($log_entry) . "\n";
file_put_contents(LOGS_DIR.'/security.log', $log_line, FILE_APPEND | LOCK_EX);
// Kritische Ereignisse
if (in_array($event_type, array('ADMIN_LOGIN_FAILED', 'SQL_INJECTION_ATTEMPT', 'XSS_ATTEMPT'))) {
send_security_alert($event_type, $log_entry);
}
}
// Alarme für kritische Ereignisse senden
function send_security_alert($event_type, $log_entry) {
global $conf;
$subject = 'Sicherheitsalarm: ' . $event_type;
$message = "Sicherheitsereignis erkannt:\n\n";
$message .= "Typ: " . $event_type . "\n";
$message .= "Zeit: " . $log_entry['timestamp'] . "\n";
$message .= "IP: " . $log_entry['ip'] . "\n";
$message .= "User Agent: " . $log_entry['user_agent'] . "\n";
$message .= "Details: " . json_encode($log_entry['data'], JSON_PRETTY_PRINT);
mail($conf['admin_email'], $subject, $message);
}
Überwachung verdächtiger Aktivitäten
// Verdächtige Muster analysieren
function analyze_suspicious_activity($ip) {
global $db, $prefix;
$suspicious_score = 0;
$last_hour = time() - 3600;
// Häufige fehlgeschlagene Login-Versuche
$stmt = $db->prepare("SELECT COUNT(*) FROM {$prefix}_login_attempts WHERE ip = ? AND success = 0 AND timestamp > ?");
$stmt->bind_param("si", $ip, $last_hour);
$stmt->execute();
$failed_logins = $stmt->get_result()->fetch_row()[0];
if ($failed_logins > 10) $suspicious_score += 3;
elseif ($failed_logins > 5) $suspicious_score += 2;
elseif ($failed_logins > 3) $suspicious_score += 1;
// Häufige Anfragen an nicht existente Seiten
$stmt = $db->prepare("SELECT COUNT(*) FROM {$prefix}_404_log WHERE ip = ? AND timestamp > ?");
$stmt->bind_param("si", $ip, $last_hour);
$stmt->execute();
$not_found_requests = $stmt->get_result()->fetch_row()[0];
if ($not_found_requests > 20) $suspicious_score += 2;
elseif ($not_found_requests > 10) $suspicious_score += 1;
// SQL-Injection-Versuche
$stmt = $db->prepare("SELECT COUNT(*) FROM {$prefix}_security_log WHERE ip = ? AND event_type = 'SQL_INJECTION_ATTEMPT' AND timestamp > ?");
$stmt->bind_param("si", $ip, $last_hour);
$stmt->execute();
$sql_attempts = $stmt->get_result()->fetch_row()[0];
if ($sql_attempts > 0) $suspicious_score += 5;
// Automatische Blockierung bei hohem Verdachtswert
if ($suspicious_score >= 5) {
block_ip($ip, 'Verdächtige Aktivität erkannt');
log_security_event('IP_AUTO_BLOCKED', array('ip' => $ip, 'score' => $suspicious_score));
}
return $suspicious_score;
}
Konfigurationssicherheit
Konfigurationsdateien schützen
# .htaccess zum Schutz des config-Ordners
<Files "*.php">
Order Deny,Allow
Deny from all
Allow from 127.0.0.1
</Files>
<Files "*.txt">
Order Deny,Allow
Deny from all
</Files>
<Files "*.log">
Order Deny,Allow
Deny from all
</Files>
Sichere Dateiberechtigungen
# Richtige Dateiberechtigungen setzen
find /path/to/slaed/ -type f -exec chmod 644 {} \;
find /path/to/slaed/ -type d -exec chmod 755 {} \;
# Spezielle Berechtigungen für kritische Ordner
chmod 700 /path/to/slaed/config/
chmod 777 /path/to/slaed/uploads/
chmod 777 /path/to/slaed/storage/
# Ausführung im Uploads-Ordner deaktivieren
chmod -x /path/to/slaed/uploads/*
Sicherheitsempfehlungen
Regelmäßige Sicherheitsprüfungen
- Protokollprüfung - Tägliche Analyse der Sicherheitsprotokolle
- Updates - Zeitnahe PHP-, MySQL-, Webserver-Updates
- Überwachung - Kontrolle verdächtiger Aktivitäten
- Backups - Regelmäßige Sicherung
- Tests - Periodische Penetrationstests
Sicherheits-Checkliste
- PHP auf neueste Version aktualisiert
- Neueste Sicherheitsupdates installiert
- Webserver-Firewall konfiguriert
- HTTPS/SSL aktiviert
- Sichere HTTP-Header konfiguriert
- Unbenutzte PHP-Module und Funktionen deaktiviert
- Sicherheitsprotokollierung konfiguriert
- Überwachungslösungen eingerichtet
- Verwundbarkeitstests durchgeführt
- Automatische Backups konfiguriert
Durch das Befolgen dieser Empfehlungen und die Nutzung der integrierten Schutzmechanismen von SLAED CMS gewährleisten Sie ein hohes Sicherheitsniveau für Ihre Website.