= $min && $len <= $max; default: return true; } } /** * Check if password meets requirements */ public static function validatePassword($password) { $errors = []; if (strlen($password) < 8) { $errors[] = 'Password must be at least 8 characters long'; } if (!preg_match('/[A-Z]/', $password)) { $errors[] = 'Password must contain at least one uppercase letter'; } if (!preg_match('/[a-z]/', $password)) { $errors[] = 'Password must contain at least one lowercase letter'; } if (!preg_match('/[0-9]/', $password)) { $errors[] = 'Password must contain at least one number'; } return empty($errors) ? true : $errors; } /** * Rate limiting */ public static function checkRateLimit($identifier, $max_attempts = 5, $time_window = 300) { if (session_status() === PHP_SESSION_NONE) { session_start(); } $key = 'rate_limit_' . md5($identifier); $now = time(); if (!isset($_SESSION[$key])) { $_SESSION[$key] = []; } // Clean old attempts $_SESSION[$key] = array_filter($_SESSION[$key], function($timestamp) use ($now, $time_window) { return ($now - $timestamp) < $time_window; }); if (count($_SESSION[$key]) >= $max_attempts) { return false; } $_SESSION[$key][] = $now; return true; } /** * Secure session management */ public static function startSecureSession() { if (session_status() === PHP_SESSION_NONE) { // Secure session configuration ini_set('session.cookie_httponly', 1); ini_set('session.cookie_secure', isset($_SERVER['HTTPS'])); ini_set('session.use_strict_mode', 1); session_start(); // Regenerate session ID on login if (!isset($_SESSION['initiated'])) { session_regenerate_id(true); $_SESSION['initiated'] = true; } } } } class OSWebErrorHandler { /** * Display user-friendly error message */ public static function displayError($message, $type = 'danger') { $icon_map = [ 'success' => 'check-circle', 'info' => 'info-circle', 'warning' => 'exclamation-triangle', 'danger' => 'x-circle' ]; $icon = $icon_map[$type] ?? 'info-circle'; return ''; } /** * Log error securely */ public static function logError($message, $context = []) { $log_entry = [ 'timestamp' => date('Y-m-d H:i:s'), 'message' => $message, 'context' => $context, 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown', 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown' ]; error_log('OSWeb Error: ' . json_encode($log_entry)); } /** * Handle database errors gracefully */ public static function handleDatabaseError($connection, $query = null) { if ($connection && mysqli_errno($connection)) { $error = mysqli_error($connection); self::logError('Database Error', [ 'error' => $error, 'query' => $query, 'errno' => mysqli_errno($connection) ]); return self::displayError('A database error occurred. Please try again later.'); } return null; } /** * Validate form with multiple fields */ public static function validateForm($data, $rules) { $errors = []; foreach ($rules as $field => $field_rules) { $value = $data[$field] ?? ''; foreach ($field_rules as $rule => $options) { if (is_numeric($rule)) { $rule = $options; $options = []; } if (!OSWebSecurity::validateInput($value, $rule, $options)) { $field_name = ucfirst(str_replace('_', ' ', $field)); switch ($rule) { case 'required': $errors[$field] = $field_name . ' is required'; break; case 'email': $errors[$field] = $field_name . ' must be a valid email address'; break; case 'int': $errors[$field] = $field_name . ' must be a valid number'; break; case 'length': $min = $options['min'] ?? 0; $max = $options['max'] ?? 'unlimited'; $errors[$field] = $field_name . " must be between {$min} and {$max} characters"; break; case 'uuid': $errors[$field] = $field_name . ' must be a valid UUID'; break; case 'avatar_name': $errors[$field] = $field_name . ' must be 2-31 characters, letters and numbers only'; break; default: $errors[$field] = $field_name . ' is invalid'; } break; // Stop at first error for this field } } } return $errors; } } class OSWebDatabase { private $connection; public function __construct() { try { $this->connection = new mysqli(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME); if ($this->connection->connect_error) { throw new Exception("Connection failed: " . $this->connection->connect_error); } $this->connection->set_charset("utf8"); } catch (Exception $e) { OSWebErrorHandler::logError('Database connection failed', ['error' => $e->getMessage()]); throw $e; } } public function prepare($query) { $stmt = $this->connection->prepare($query); if (!$stmt) { OSWebErrorHandler::logError('Prepare failed', [ 'query' => $query, 'error' => $this->connection->error ]); throw new Exception("Prepare failed: " . $this->connection->error); } return $stmt; } public function getConnection() { return $this->connection; } public function close() { if ($this->connection) { $this->connection->close(); } } public function __destruct() { $this->close(); } } // Helper functions for backward compatibility function sanitize_input($input, $type = 'string') { return OSWebSecurity::sanitizeInput($input, $type); } function display_error($message, $type = 'danger') { return OSWebErrorHandler::displayError($message, $type); } function validate_form($data, $rules) { return OSWebErrorHandler::validateForm($data, $rules); } function csrf_token_field() { $token = OSWebSecurity::generateCSRFToken(); return ''; } function verify_csrf_token() { $token = $_POST['csrf_token'] ?? ''; return OSWebSecurity::verifyCSRFToken($token); } ?>