# Безпека

Безпека в 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`.
