Security Guide
SLAED CMS is developed with a focus on security and includes a multi-layered protection system against major types of attacks and threats.
Table of Contents
Security Architecture
Security Principles
- Defense in Depth - Multi-layered protection
- Principle of Least Privilege - Minimum necessary rights
- Input Validation - Validation of all input data
- Output Encoding - Encoding all output data
- Secure by Default - Secure settings by default
Main Protection Components
┌─────────────────────────────────────────┐
│ SECURITY LAYERS │
├─────────────────────────────────────────┤
│ 1. Web Server Security (Apache/Nginx) │
│ 2. Application Firewall │
│ 3. Input Validation & Sanitization │
│ 4. Authentication & Authorization │
│ 5. Session Management │
│ 6. CSRF Protection │
│ 7. XSS Prevention │
│ 8. SQL Injection Prevention │
│ 9. File Upload Security │
│ 10. Logging & Monitoring │
└─────────────────────────────────────────┘
Protection Against Main Attacks
1. SQL Injection Protection
Using Prepared Statements
// SAFE - 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();
// UNSAFE - Direct substitution (DON'T USE!)
$query = "SELECT * FROM {$prefix}_users WHERE user_id = " . $user_id;
Automatic Filtering in Core Functions
// getVar function automatically filters input data
$id = getVar('get', 'id', 'num'); // Numbers only
$email = getVar('post', 'email', 'email'); // Email validation
$text = getVar('post', 'text', 'text'); // Safe text
2. XSS (Cross-Site Scripting) Protection
Input Filtering
// Automatic filtering 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);
}
}
// Additional protection when outputting
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 template files
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) Protection
Token Generation and Verification
// Generate CSRF token
function generate_csrf_token() {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
// Verify CSRF token
function verify_csrf_token($token) {
if (!isset($_SESSION['csrf_token'])) {
return false;
}
return hash_equals($_SESSION['csrf_token'], $token);
}
// Usage in forms
echo '<input type="hidden" name="csrf_token" value="'.generate_csrf_token().'">';
// Verification when processing form
if (!verify_csrf_token(getVar('post', 'csrf_token', 'text'))) {
die('CSRF token mismatch');
}
4. File Upload Security
Strict File Upload Checking
function secure_file_upload($file, $allowed_types, $max_size, $upload_dir) {
// 1. Check file type by extension
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowed_types)) {
return false;
}
// 2. Check MIME type
$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. Check size
if ($file['size'] > $max_size) {
return false;
}
// 4. Check for executable files
$dangerous_extensions = array('php', 'phtml', 'php3', 'php4', 'php5', 'pl', 'py', 'jsp', 'asp', 'sh', 'cgi');
if (in_array($ext, $dangerous_extensions)) {
return false;
}
// 5. Generate safe filename
$safe_name = uniqid().'_'.preg_replace('#[^a-zA-Z0-9.-]#', '_', basename($file['name']));
// 6. Move file
if (move_uploaded_file($file['tmp_name'], $upload_dir.'/'.$safe_name)) {
return $safe_name;
}
return false;
}
Additional Security Measures
# .htaccess in uploads folder
<Files "*.php">
Deny from all
</Files>
<Files "*.phtml">
Deny from all
</Files>
<Files "*.php3">
Deny from all
</Files>
Authentication & Authorization
Role and Access System
// User hierarchy
define('USER_GUEST', 0); // Guest
define('USER_MEMBER', 1); // User
define('USER_MODERATOR', 2); // Moderator
define('USER_ADMIN', 3); // Administrator
define('USER_SUPERADMIN', 4); // Super Administrator
// Access level check
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;
}
// Module admin check
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;
}
Secure Authentication
// Password hashing
function hash_password($password) {
return password_hash($password, PASSWORD_ARGON2ID, [
'memory_cost' => 65536, // 64MB
'time_cost' => 4, // 4 iterations
'threads' => 3, // 3 threads
]);
}
// Password verification
function verify_password($password, $hash) {
return password_verify($password, $hash);
}
// Password strength check
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
}
Session Management
// Secure session settings
function init_secure_session() {
// Session cookie settings
$cookie_params = array(
'lifetime' => 0,
'path' => '/',
'domain' => '',
'secure' => isset($_SERVER['HTTPS']),
'httponly' => true,
'samesite' => 'Strict'
);
session_set_cookie_params($cookie_params);
// Regenerate session ID
if (!isset($_SESSION['initiated'])) {
session_regenerate_id(true);
$_SESSION['initiated'] = true;
}
// Check IP address and User Agent
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;
}
// Secure session termination
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();
}
Protection Against Automated Attacks
Rate Limiting / Flood Protection
// Flood protection
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']);
// Count attempts
$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'])) {
// Log attempt
log_security_event('FLOOD_DETECTED', array(
'action' => $action,
'ip' => $ip,
'user_id' => $user_id,
'attempts' => $attempts
));
return false;
}
// Record attempt
$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;
}
Security Monitoring & Logging
Logging System
// Log security events
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);
// Critical events
if (in_array($event_type, array('ADMIN_LOGIN_FAILED', 'SQL_INJECTION_ATTEMPT', 'XSS_ATTEMPT'))) {
send_security_alert($event_type, $log_entry);
}
}
// Send alerts for critical events
function send_security_alert($event_type, $log_entry) {
global $conf;
$subject = 'Security Alert: ' . $event_type;
$message = "Security event detected:\n\n";
$message .= "Type: " . $event_type . "\n";
$message .= "Time: " . $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);
}
Monitoring Suspicious Activity
// Analyze suspicious patterns
function analyze_suspicious_activity($ip) {
global $db, $prefix;
$suspicious_score = 0;
$last_hour = time() - 3600;
// Frequent failed login attempts
$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;
// Frequent requests to non-existent pages
$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 attempts
$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;
// Automatic blocking for high suspicious score
if ($suspicious_score >= 5) {
block_ip($ip, 'Suspicious activity detected');
log_security_event('IP_AUTO_BLOCKED', array('ip' => $ip, 'score' => $suspicious_score));
}
return $suspicious_score;
}
Configuration Security
Protecting Configuration Files
# .htaccess to protect config folder
<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>
Secure File Permissions
# Set proper file permissions
find /path/to/slaed/ -type f -exec chmod 644 {} \;
find /path/to/slaed/ -type d -exec chmod 755 {} \;
# Special permissions for critical folders
chmod 700 /path/to/slaed/config/
chmod 777 /path/to/slaed/uploads/
chmod 777 /path/to/slaed/storage/
# Disable execution in uploads folder
chmod -x /path/to/slaed/uploads/*
Security Recommendations
Regular Security Checks
- Log Audit - Daily analysis of security logs
- Updates - Timely PHP, MySQL, web server updates
- Monitoring - Control of suspicious activity
- Backups - Regular backup
- Testing - Periodic penetration testing
Security Checklist
- Updated PHP to latest version
- Installed latest security updates
- Configured web server firewall
- Enabled HTTPS/SSL
- Configured secure HTTP headers
- Disabled unused PHP modules and functions
- Configured security logging
- Set up monitoring solutions
- Conducted vulnerability testing
- Configured automatic backups
By following these recommendations and using the built-in protection mechanisms of SLAED CMS, you ensure a high level of security for your website.