Безопасность

Безопасность

Безопасность в Celena не опциональна — это набор обязательных практик. Ниже — что использовать и чего не делать.

Валидация ввода

Любой пользовательский ввод проходит через Celena\Core\Security\Filter:

use Celena\Core\Security\Filter;

$name  = Filter::string((string) $request->input('name'));
$slug  = Filter::urlSlug((string) $request->input('slug'));
$email = Filter::email((string) $request->input('email'));
$id    = (int) $request->input('id');

Не доверяйте $_GET/$_POST напрямую — берите через $request->input()/query() и фильтруйте.

SQL-инъекции

Только подготовленные выражения и QueryBuilder. Конкатенация значений в SQL запрещена.

// ПРАВИЛЬНО
$conn->builder('users')->where('email', '=', $email)->first();
$conn->run('SELECT * FROM ' . $conn->table('users') . ' WHERE id = ?', [$id]);

// ОПАСНО — так нельзя
$conn->run("SELECT * FROM users WHERE email = '$email'");

XSS — вывод в HTML

В шаблонах переменные экранируются автоматически ({var}). Сырой вывод ({var|raw}) — только для доверенного HTML. В PHP используйте хелпер e():

echo e($userText);          // безопасно
echo $userText;             // ОПАСНО, если это ввод пользователя

Markdown-рендерер ядра экранирует исходный HTML — пользовательский markdown не может выполнить <script>.

CSRF

Формы и AJAX-запросы защищены токеном. В шаблоне:

<form method="post">{csrf} … </form>     <!-- скрытое поле _csrf -->
{csrf format="meta"}                       <!-- мета-тег для fetch/AJAX -->

На сервере:

use Celena\Core\Security\Csrf;
if (!Csrf::validate((string) $request->input('_csrf'))) {
    return Response::json(['error' => 'csrf'], 419);
}

Аутентификация и роли

  • Пароли хранятся как Argon2id (Celena\Core\Security\Password). В открытом виде не хранятся нигде.
  • Вход — /admin/login. Пользователи — в таблице cl_celena_users.
  • Роли: admin (всё), editor, author, user, customer. Проверка прав:
use Celena\Core\Security\Permission;
if (!Permission::can($user, 'news.update')) {
    return Response::redirect('/admin/login');
}

Админские контроллеры проверяют $this->auth->user() в начале действия.

Ограничение частоты

Celena\Core\Security\RateLimiter защищает чувствительные ручки (логин, приём заявок, публичный API) от перебора:

$limiter = $container->make(RateLimiter::class);
if (!$limiter->attempt('login_' . $request->ip(), 5, 60)) {  // 5 попыток в минуту
    return Response::json(['error' => 'rate_limited'], 429);
}

Загрузка файлов и zip

  • Проверяйте MIME/расширение загружаемых файлов; не отдавайте их как исполняемые.
  • Установщик плагинов проверяет архивы на ZIP-Slip (выход за пределы папки), блокирует .phar, валидирует manifest.json. Эти же проверки повторяйте при любой распаковке пользовательских архивов.

CSP и внешние ресурсы

Политика безопасности контента (CSP) пускает скрипты только с 'self' и доверенных доменов (Google-сервисы). Внешние CDN (jQuery, шрифты, виджеты) заблокированы — храните ассеты локально. На сайте нет jQuery.

.htaccess и сервер

Не используйте в .htaccess директивы <Directory> или php_flag при работе с PHP-FPM — это ломает Apache (ошибка 500). Кеш-заголовки статики настраиваются на уровне nginx, а не в .htaccess.