From 934972ce4885abf3cddfc1c840631eddeb1e890d Mon Sep 17 00:00:00 2001 From: Mikhail Yenuchenko Date: Sun, 2 Aug 2026 21:38:40 +0300 Subject: [PATCH] docs: drop the closed phase-14 hardening out of the plan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The plan holds only open work, but section A still retold what phase 14 implemented — security headers, the origin check, the __Host- cookie, the setup-token documentation — which the CHANGELOG and git history already describe in full. What is left of A is the two things deliberately *not* closed: the POST that carries neither Sec-Fetch-Site nor Origin, and the absence of session-bound CSRF tokens. The XSS note folds into the token item, which is the only place it was doing work; the "proxy must pass Host through" requirement is documented in the README and in the CHANGELOG, so it goes. Section A's numbered items are gone with it, so B-D renumber from 1. Nothing else in the file has been implemented: the README still says nothing about restarts signing the admin out or about the copytruncate rotation window, SELFPOST_HOSTNAME still falls back to localhost without a warning, and CI still runs only vet and unit tests. Co-Authored-By: Claude Opus 5 --- docs/implementation-plan.md | 46 +++++++++++++++---------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/docs/implementation-plan.md b/docs/implementation-plan.md index b19ec80..8ecc972 100644 --- a/docs/implementation-plan.md +++ b/docs/implementation-plan.md @@ -11,20 +11,16 @@ ## Открытые вопросы — требует внимания и обсуждения (перед фиксацией v1.0) -Базовый план 0→11 выполнен и **соответствует ТЗ** (все обязательные пункты 7.6 -подтверждены аудитом Фазы 11). Ниже — то, что **выходит за букву ТЗ**, но -заслуживает решения перед тем, как считать v1.0 «финальным». Ничего из этого -**не является дефектом соответствия**; это осознанные компромиссы и -потенциальные улучшения. Каждый пункт — решение «делаем в v1.x / откладываем в -2.x / оставляем как есть», принимается пользователем. +Ниже — то, что **выходит за букву ТЗ**, но заслуживает решения перед тем, как +считать v1.0 «финальным». Ничего из этого **не является дефектом +соответствия**; это осознанные компромиссы и потенциальные улучшения. Каждый +пункт — решение «делаем в v1.x / откладываем в 2.x / оставляем как есть», +принимается пользователем. -### A. Безопасность — hardening сверх обязательного 7.6 +### A. Безопасность — принятые риски -Все четыре пункта раздела — security-заголовки, проверка origin, cookie -`__Host-` с обнаружением дублей и документация про `/data/setup-token` — -**реализованы (Фаза 14)**; что именно сделано, см. [CHANGELOG.md](../CHANGELOG.md) -и `git log`. Здесь остаётся только то, что закрыто **сознательно не было**, -чтобы это не потерялось: +Hardening сверх обязательного 7.6 закрыт. Здесь остаётся только то, что закрыто +**сознательно не было**, чтобы это не потерялось: - **Принятый риск: `POST` без `Sec-Fetch-Site` и без `Origin` пропускается.** Клиент, не посылающий ни одного из двух — по-настоящему старый браузер или @@ -38,31 +34,25 @@ закрывает соседний поддомен, но зависит от поведения браузера; токен — нет. Цена — скрытое поле примерно в двух десятках форм. Триггером вернуться к вопросу считать появление требования «устойчиво независимо от браузера». -- **XSS внутри самой панели** не закрывается ни проверкой origin, ни токенами: - код, исполняющийся в origin панели, отправит запрос сам. Против него - работают автоэкранирование `html/template` (7.6.7) и CSP — поэтому шаблоны - не должны содержать inline-скриптов и inline-стилей; это закреплено - тестом-стражем в [internal/web/templates_test.go](../internal/web/templates_test.go), - а не только договорённостью. -- **Требование к развёртыванию, появившееся вместе с проверкой origin:** - reverse-proxy обязан передавать исходный заголовок `Host`. Все четыре - поставляемых фрагмента это делают; чужой прокси, переписывающий `Host`, - превратит каждый `POST` в `403` (в лог пишутся обе сравниваемые стороны). + От XSS внутри самой панели не спас бы и токен: код, исполняющийся в origin + панели, отправит запрос сам — против этого работают автоэкранирование + `html/template` (7.6.7) и CSP, поэтому шаблоны не должны содержать + inline-скриптов и inline-стилей. ### B. Надёжность и эксплуатация -5. **Сессии только в памяти** — рестарт/редеплой разлогинивает админа (по ТЗ 9 допустимо). Плюс: absolute TTL 12ч без отдельного idle-timeout и без ротации токена при логине (session-fixation здесь неактуален, т.к. токен выдаётся только после аутентификации). Оставить; отметить поведение в README. -6. **Окно потери строк мониторингового лога при ротации** (`copytruncate`, Фаза 10) — несколько строк `mail.log` могут потеряться в момент ротации. Приемлемо для мониторинга; зафиксировать как известное свойство. -7. **Поведение при незаданном `SELFPOST_HOSTNAME`** — realm SASL и хост setup-ссылки падают в `localhost`. Для реального деплоя hostname обязателен. **Вопрос:** делать ли фатальную проверку «hostname обязателен» на старте (сейчас — мягкий fallback) — предложение: предупреждать громко в лог, но не падать. +1. **Сессии только в памяти** — рестарт/редеплой разлогинивает админа (по ТЗ 9 допустимо). Плюс: absolute TTL 12ч без отдельного idle-timeout и без ротации токена при логине (session-fixation здесь неактуален, т.к. токен выдаётся только после аутентификации). Оставить; отметить поведение в README. +2. **Окно потери строк мониторингового лога при ротации** (`copytruncate`, Фаза 10) — несколько строк `mail.log` могут потеряться в момент ротации. Приемлемо для мониторинга; зафиксировать как известное свойство. +3. **Поведение при незаданном `SELFPOST_HOSTNAME`** — realm SASL и хост setup-ссылки падают в `localhost`. Для реального деплоя hostname обязателен. **Вопрос:** делать ли фатальную проверку «hostname обязателен» на старте (сейчас — мягкий fallback) — предложение: предупреждать громко в лог, но не падать. ### C. CI и тесты -8. **Нет интеграционного/e2e-теста в CI.** Контейнерные e2e каждой фазы прогонялись вручную и задокументированы в git-истории, но не автоматизированы. Для v1.0 — вероятно, оставить ручными; для долгой поддержки — кандидат на smoke-тест (поднять контейнер, setup→login→add domain→auth SMTP) в CI. +4. **Нет интеграционного/e2e-теста в CI.** Контейнерные e2e каждой фазы прогонялись вручную и задокументированы в git-истории, но не автоматизированы. Для v1.0 — вероятно, оставить ручными; для долгой поддержки — кандидат на smoke-тест (поднять контейнер, setup→login→add domain→auth SMTP) в CI. ### D. Указатель на объём 2.x -9. **Входящий релей и pluggable-антиспам** вынесены в опциональные фазы O1+ ниже (линия 2.x.x, вне v1.0, только по согласованию — ТЗ 12.6). Здесь перечислены лишь как напоминание, что это **сознательно отложенный объём**, а не забытый. -10. **2FA и несколько администраторов** — вне объёма v1.x (ТЗ этого не требует: один админ). Кандидаты на 2.x, если понадобятся. +5. **Входящий релей и pluggable-антиспам** вынесены в опциональные фазы O1+ ниже (линия 2.x.x, вне v1.0, только по согласованию — ТЗ 12.6). Здесь перечислены лишь как напоминание, что это **сознательно отложенный объём**, а не забытый. +6. **2FA и несколько администраторов** — вне объёма v1.x (ТЗ этого не требует: один админ). Кандидаты на 2.x, если понадобятся. ---