Skip to content

Безопасность и права

Статус: решения зафиксированы (Q3, Q10, Q11 брейншторма). Детали реализации уточняются в фазе 0. Урок, который держим в голове: 91% CVE в экосистеме WordPress — в плагинах, а не в ядре. Значит, контракт модуля и модель прав важнее, чем ядро.

Модель прав (RBAC)

Filament Shield поверх spatie/laravel-permission. Уже в стеке студии, стандарт де-факто. Shield генерирует permission'ы по Filament-ресурсам автоматически.

Базовые роли парка:

РольДоступ
Разработчик студииПолный: код, модули, обновления, все сайты
Контент-менеджер клиентаКонтент, страницы, заявки, настройки своего сайта
РедакторОграниченный: только назначенные разделы контента

Многоуровневая ролевая модель (Twill Role/RoleGroup/RoleGroupItem) — не нужна для 3–4 ролей. Если у конкретного клиента вырастут сложные роли — это модуль «расширенные роли», не переделка ядра.

Принцип «модуль ≠ ядро по правам»

Ключевое архитектурное правило, заложенное с фазы 0: модуль не может молча расширить свои привилегии до уровня ядра.

  • Модуль декларирует свои permission'ы в манифесте — Shield их подхватывает, но они видны и управляемы администратором.
  • Модуль не пишет в таблицы ядра напрямую — только через сервисы/события ядра.
  • Модуль не переопределяет политики ядра — только добавляет свои.
  • Capability-слой: что модулю можно (свои роуты, свои таблицы, свои события), что нельзя (трогать пользователей ядра, менять чужие права) — в контракте модуля.

Это заготовка под будущий открытый маркетплейс: когда/если пустим сторонние модули, модель «песочницы прав» уже будет на месте. Ретрофитить её в открытый маркетплейс невозможно — поэтому закладываем сразу, хотя маркетплейс пока закрыт.

Маркетплейс: закрыт сейчас, архитектура под открытие

Сейчас маркетплейс закрытый — только модули студии, внутренний каталог Satis. Сторонние разработчики не публикуют.

Почему не открываем сразу: ревью безопасности чужого кода силами одного разработчика неподъёмно. Открытый маркетплейс без обязательного ревью = один уязвимый модуль ставит под удар весь парк managed-сайтов (урок WordPress).

Что закладываем под будущее открытие (без переделки потом):

  • контракт модуля с capability-слоем (см. выше);
  • модель лицензирования по клиенту (license key на сайт — Statamic-модель);
  • политику «плагин ≠ ядро по правам»;
  • изоляцию сбоев (баг в модуле не роняет ядро).

Открытие маркетплейса сторонним — фаза 5, не блокирует ядро. Модель ревью безопасности (кто отвечает за чужой код) решается тогда же.

Лицензирование клиенту

При расторжении подписки на поддержку:

ЧтоПередаётся клиенту
Код сайта (skeleton + тема + контент + БД)✅ Да — сайт продолжает работать
Доступ к Satis (обновления ядра/модулей)❌ Нет — отключается

Модель: клиент владеет своим сайтом (не заложник SaaS), но поток обновлений и модулей студии — платная услуга. Проговорить в договоре: пункт о передаче кода и прекращении доступа к обновлениям.

Техническое следствие: skeleton должен быть самодостаточным — composer-пакеты ядра и модулей вендорятся в поставку клиента, чтобы сайт работал без Satis. На стороне Satis — управление доступом по клиенту (токен/ключ), отзываемый при расторжении.

Фильтрация входа на всех границах (ядро)

Принцип OWASP: валидация на входе, экранирование на выходе, и ни одна граница не доверяет предыдущей. Границ у CMS пять, у каждой — свой обязательный фильтр ядра:

ГраницаФильтр (не на совести разработчика модуля)
Формы (публичные + конструктор Q8)FormRequest из движка полей: типы, rules, maxLength; CSRF, honeypot, rate-limit, согласие ПДн
API (/api/vN)тот же движок полей → 422; whitelisted-фильтрация spatie/laravel-query-builder (произвольный SQL/поле в запросе невозможны); конверт ошибок без stack trace
Админка (Filament)те же rules из декларации поля; RBAC/Policy на каждое действие; никакого «доверенного» ввода — контент-менеджер клиента не админ студии
Файлыwhitelist MIME+расширений по типу поля, лимит размера, пересборка изображений (Intervention re-encode срезает полезную нагрузку), приватный диск для документов, раздача через подписанные URL
Внешние ответы (webhooks, интеграции)верификация подписи (HMAC + timestamp против replay), схема-валидация payload, outbound-http только через integration-core (SSRF-защита: запрет внутренних адресов)

Правила сквозные:

  • rich-text — санитизация HTML по whitelist-тегам при сохранении и при выводе (двойной барьер); {!! !!} — только для санитизированных полей из whitelist, линтер контракта это проверяет;
  • регулярные выражения: пользовательские regex (правила редиректов is_regex, кастомные rules полей) — источник ReDoS. Ядро валидирует паттерн при сохранении (компилируемость + эвристика катастрофического бэктрекинга) и исполняет с лимитом (preg_* + backtrack_limit, таймаут-обёртка). Произвольный regex от контент-менеджера клиента — запрещён capability-моделью, только роль студии;
  • выход — только ; JSON-ответы через API Resources (нет утечки скрытых полей); заголовки безопасности (CSP через spatie/laravel-csp, X-Frame-Options, nosniff) — middleware ядра с конфигом. Механика CSP — ревизия ядра №2: nonce для инлайн-вставок (pixels/analytics уже рассчитывают на него), источники собираются каноническим фильтром security.csp (тема — из theme.json, модули — фильтром), режимы off/report-only/enforce в группе настроек security (внедрение всегда через report-only), SRI на ассеты темы.

Модуль «Статистика атак» (attack-monitor)

Обязательный инфра-модуль managed-парка (в одном ряду с бэкапом и health-check): CMS видит, что её атакуют, сама замедляет атакующего и отчитывается студии.

Детект (источники сигналов):

  • паттерны в запросах: SQLi/XSS/path-traversal сигнатуры, зонды по чужим CMS (/wp-login.php, /bitrix/admin), всплеск 404 (сканер), невалидные подписи вебхуков;
  • брутфорс: серии провалов логина по IP/аккаунту (admin, кабинеты, API-токены);
  • флуд форм: срабатывания honeypot/капчи, превышения rate-limit, спам-паттерны в лидах;
  • аномалии: резкий рост RPS с одного IP/подсети, перебор ID/slug (enumeration).

Реакция (эскалация по ступеням):

  1. rate-limit — штатный троттлинг (уже в ядре) — первый барьер;
  2. временная блокировка IP/аккаунта на уровне приложения (minutes → hours при повторе), капча-челлендж вместо жёсткого бана для серых случаев;
  3. эскалация на периметр: экспорт блок-листа для Caddy (reverse proxy студии уже терминирует весь трафик — баним до PHP, дёшево) — файл/API, который Caddy перечитывает; аналог fail2ban-петли;
  4. алерт: Telegram/email студии при превышении порогов; событие в телеметрию парка (флит-дашборд видит волну атак по всем сайтам — признак таргетированной кампании).

Статистика (Filament-дашборд):

  • журнал attack_events: время, IP/ASN/гео, тип сигнала, URL, вердикт (logged/challenged/ blocked), TTL блокировки;
  • агрегаты: топ-IP, топ-паттернов, динамика по дням, «до/после» блокировок;
  • ретеншн и выгрузка (для абуз-жалоб хостеру/RKN-отчётности);
  • ложные срабатывания: whitelist IP (офис клиента, мониторинг), разбан в один клик.

Границы модуля: это не полноценный WAF (не подменяет Caddy/CDN-защиту от L7-DDoS) — это прикладной слой: то, что видно только приложению (логины, формы, бизнес-аномалии), плюс статистика и эскалация. Донор паттернов: антифрод catalog/er (детект накрутки), freelance/project/src/app/Domains/Reputation/ (репутационные сигналы).

Безопасность как набор подсистем

Безопасность — не одна фича, а слой подсистем (см. каталог подсистем). В ядре — базовое (аутентификация, RBAC, политики, rate-limit, защита форм, фильтрация границ — выше). Остальное — модули под потребность сайта:

  • Attack-monitor — статистика атак и автоблокировка (см. выше), обязателен в парке;
  • 2FA (er, freelance) — двухфакторка для админки/кабинета;
  • OAuth/соцвход — вход через Google/VK/Яндекс;
  • Антиспам/антифрод (catalog, er) — защита форм и отзывов;
  • Согласия/152-ФЗ — cookie-баннер, согласия на обработку ПДн, экспорт/удаление;
  • Аудит действий (freelance, referendum) — журнал «кто что изменил»;
  • Шифрование полей — encrypted casts в ядре;
  • API-токены (Sanctum) — для внешнего доступа.

Чеклист безопасности ядра (фаза 0)

  • [ ] Capability-слой модуля в контракте — что модулю можно/нельзя.
  • [ ] Политика «модуль не пишет в таблицы ядра напрямую».
  • [ ] Shield-декларация permission'ов обязательна для модуля.
  • [ ] Rate-limit + CSRF + honeypot на всех формах ядра (не на совести клиента).
  • [ ] Фильтрация пяти границ (формы/API/админка/файлы/вебхуки) — движок полей + query-builder + подписи.
  • [ ] Санитизация rich-text на входе и выходе; {!! !!} только по whitelist (линтер).
  • [ ] Валидация пользовательских regex (ReDoS): проверка паттерна + лимиты исполнения; произвольный regex — только роль студии.
  • [ ] Заголовки безопасности (CSP, X-Frame-Options, nosniff) — middleware ядра; CSP с nonce, источники через фильтр security.csp, внедрение через report-only.
  • [ ] Загрузки сканируются при включённой реализации upload-scanner (карантин pending до вердикта); 0 реализаций — штатная деградация.
  • [ ] Attack-monitor: журнал атак, автоблокировка со ступенями, экспорт блок-листа в Caddy, алерты, дашборд.
  • [ ] Базовое согласие ПДн (чекбокс + consent_at на лиде) — в ядре; расширенное — модуль 152-ФЗ.
  • [ ] 2FA для ролей студии — обязательная политика managed-парка (модуль 2FA ставится всем сайтам по умолчанию: учётка разработчика открывает весь парк).
  • [ ] Секреты — только .envconfig(), никогда env() в коде модуля.
  • [ ] Satis — управление доступом по клиенту (отзыв при расторжении).
  • [ ] Обновления через worker/CLI, не HTTP (см. центр обновлений).

Внутренняя база знаний студии