Безопасность
Безопасность
Безопасность в 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.