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

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

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

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

```php
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 запрещена.**

```php
// ПРАВИЛЬНО
$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()`:

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

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

## CSRF

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

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

На сервере:

```php
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`. Проверка прав:

```php
use Celena\Core\Security\Permission;
if (!Permission::can($user, 'news.update')) {
    return Response::redirect('/admin/login');
}
```

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

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

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

```php
$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`.
