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;'>×</button>
</div>";
unset($_SESSION['flash']);
}
}