View file system/core.php

File size: 3.7Kb
<?php
/**
 * CMS: LaiCMS (v1.0 Edition 2026)
 * File: system/core.php
 * Level: Extreme Security Edition
 */

declare(strict_types=1);

// 1. Безопасность сессий (Anti-Hijacking)
if (session_status() === PHP_SESSION_NONE) {
    ini_set('session.cookie_httponly', '1');
    ini_set('session.use_only_cookies', '1');
    ini_set('session.cookie_samesite', 'Strict');
    session_start();
}

/**
 * 2. Улучшенная защита вывода (XSS Prevention)
 * Используем ENT_SUBSTITUTE для предотвращения обрыва строки при некорректных символах.
 */
function _e(?string $text): string {
    return htmlspecialchars($text ?? '', ENT_QUOTES | ENT_HTML5 | ENT_SUBSTITUTE, 'UTF-8');
}

/**
 * 3. Строгая проверка прав (Type-Safe Role Check)
 */
function isAdmin(): bool {
    return isset($_SESSION['role'], $_SESSION['user_id']) && $_SESSION['role'] === 'admin';
}

/**
 * 4. Криптографически стойкая CSRF защита
 * Генерируем новый токен при отсутствии и проверяем через hash_equals (защита от атак по времени).
 */
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(64)); // Увеличенная длина (512 bit)
}

function check_csrf(?string $token): bool {
    if (!$token || empty($_SESSION['csrf_token'])) {
        return false;
    }
    return hash_equals($_SESSION['csrf_token'], $token);
}

/**
 * 5. Безопасные SQL-запросы (Advanced Prepared Statements)
 * Добавлена поддержка float (d) и автоматическое определение типов.
 */
function q(string $query, array $params = []) {
    global $mysqli;
    
    if (!$mysqli) {
        throw new RuntimeException("Database connection lost.");
    }

    $stmt = $mysqli->prepare($query);
    if (!$stmt) {
        error_log("SQL Prepare Error: " . $mysqli->error);
        return false;
    }

    if ($params) {
        $types = "";
        foreach ($params as $p) {
            if (is_int($p)) $types .= 'i';
            elseif (is_float($p)) $types .= 'd';
            else $types .= 's';
        }
        $stmt->bind_param($types, ...$params);
    }

    if (!$stmt->execute()) {
        error_log("SQL Execute Error: " . $stmt->error);
        return false;
    }

    return $stmt->get_result();
}

/**
 * 6. Безопасные Flash-уведомления (Scoped Toasts)
 */
function set_flash(string $msg, string $type = 'success'): void {
    $allowed_types = ['success', 'error', 'warning', 'info'];
    $type = in_array($type, $allowed_types) ? $type : 'info';
    
    // Храним сообщение в массиве, предотвращая инъекции при записи
    $_SESSION['flash'] = [
        'msg'  => $msg, 
        'type' => $type,
        'id'   => uniqid('msg_')
    ];
}

/**
 * 7. Вывод уведомлений с защитой интерфейса
 */
function display_flash(): void {
    if (!empty($_SESSION['flash'])) {
        $f = $_SESSION['flash'];
        $type = _e($f['type']);
        $msg  = _e($f['msg']);
        
        // Современный CSS-стиль вместо инлайна
        echo "
        <div class='alert alert-{$type} animate__animated animate__fadeIn' role='alert' id='{$f['id']}'>
            <div class='alert-content'>
                <strong>" . strtoupper($type) . ":</strong> {$msg}
            </div>
            <button onclick=\"this.parentElement.remove()\" style='background:none; border:none; float:right; cursor:pointer;'>&times;</button>
        </div>";
        
        unset($_SESSION['flash']);
    }
}