Files
bare/docs/threat-model.md
mayatnikovandClaude Opus 5 db45978b16 Этап 6: закалка — лимиты ADR-021, аудит модели угроз, сверка документов
Лимиты: все четыре правила ADR-021 — регистрация 5/час на IP, вход 10/10 мин
на IP и ник, сообщения 30/мин, прочие изменяющие 60/мин; 429 с Retry-After;
X-Real-IP читается только с loopback, иначе адрес соединения — иначе заголовок
отменял бы лимит на IP; карты вёдер ограничены поколениями.

Аудит нашёл то, что пропустили пять раундов ревью:
ADR-056: nginx вёл access_log с IP и полными путями вопреки обещанию deploy.md.
Ники и социальный граф ложились в /var/log/nginx рядом с чистым журналом bare.
ADR-058: «выйти на других устройствах» не обрывал уже открытый SSE — отозванная
сессия продолжала получать сообщения.
ADR-059: промежуточный ключ комнаты был невосстановим. Участник, пропустивший
офлайн два rekey подряд, навсегда не расшифровал бы сообщения среднего ключа —
вопреки обещанию storage.md о повторной попытке после получения keyId.
ADR-063: ACK уходил по одному на конверт, а не пачкой. Получатель в оживлённой
комнате выедал общее ведро подтверждениями и упирался в 429 на всех изменяющих
запросах, включая выход из комнаты: 116 отказов за прогон стало нулём.
ADR-055, 057, 060, 061, 062: ключи вёдер и границы, +dirty у bare version,
403 unknown_device не хоронит сообщение, усечение имени в подсказке ввода,
kdf как оракул после повышения цели KDF.

Модель угроз пополнена тем, что действительно видит оператор: push-подписки
лежат в базе открытым текстом, и вместе с VAPID-ключом с той же машины это
произвольное уведомление на экране блокировки.

README приведён к v1.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DbCjVfTFq4ZFG8juD45YJ
2026-08-23 06:22:52 +03:00

46 lines
9.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Модель угроз
Честная. Здесь написано не только что Bare защищает, но и чего он защитить не может.
## Что защищаем
Содержимое сообщений. Оно шифруется на устройстве отправителя и расшифровывается на устройствах получателей. Сервер, канал и оператор видят только шифротекст.
## От кого защищаем
**Пассивный оператор сервера.** Админ с полным доступом к базе, диску и логам запросов видит: ники, argon2-хеши от `authKey`, зашифрованные ключевые блобы, имена комнат и составы, завёрнутые ключи комнат, транзитную очередь шифротекстов, push-подписки устройств — адрес push-сервиса и ключи подписки `p256dh` и `auth`. Пароль на сервер не приходит (ADR-015) — в логах запросов материала ключа нет. Плейнтекста у него нет. База вместе с VAPID-ключом, который лежит на той же машине в `/etc/bare/env`, позволяет показать устройству произвольное уведомление от имени bare — вплоть до фишингового текста на экране блокировки; содержимого сообщений это не раскрывает.
**Сетевой наблюдатель.** HTTPS обязателен. Наблюдатель видит факт и объём трафика к серверу, не содержимое.
**Кража базы или бэкапа.** В базе нет ничего сверх того, что видит оператор: те же шифротексты и метаданные.
**Push-инфраструктура.** Пуш не несёт содержимого: сервер не знает плейнтекста, поэтому его нет и в пуше.
## От кого не защищаем
**Активно-злонамеренный оператор.** Оператор, способный подменить клиентский код, может украсть ключи и плейнтекст. Это фундаментальный предел web-E2EE: клиент каждый раз загружается с сервера. Смягчение — открытый код, клиент из нескольких читаемых файлов без сборки, статика внутри бинаря, хеш которого сверяется со сборкой из тега: подмену можно заметить. Гарантии нет.
**Подмена публичного ключа.** Ключи раздаёт сервер. Защита — TOFU (ADR-016): подмена возможна только при первом контакте, дальше клиент видит смену ключа и блокирует отправку до подтверждения отпечатка. Защита работает ровно настолько, насколько люди сверяют отпечатки; если не сверяют — первый контакт остаётся на доверии к серверу.
**Подделка отправителя в комнате.** Подписей нет; `from` ставит сервер. Участник комнаты может создать валидный шифротекст, но приписать его другому — только в сговоре с сервером. В 1:1 подделка невозможна без общего секрета.
**Метаданные.** Кто, с кем, когда и сообщениями какого размера обменивается, имена комнат и их составы, список устройств, когда они появлялись и куда им слать пуши, — серверу видно. Скрытие метаданных — не задача Bare.
**Компрометация устройства.** История лежит на устройстве в открытом виде (IndexedDB), там же — приватный ключ и секрет аккаунта как non-extractable `CryptoKey`. Доступ к устройству — доступ к истории и возможность писать от имени владельца. Защита устройства — зона ответственности пользователя и ОС. XSS в клиенте — отдельный риск того же класса; смягчение — CSP без исключений и запрет `innerHTML`.
**Слабый пароль.** Пароль — материал ключа. Ключевой блоб хранится на сервере, и его стойкость к оффлайн-перебору равна стойкости пароля. Гарантия «оператор не читает сообщения» действует в пределах стойкости пароля пользователя: слабый пароль — слабое E2EE. Это осознанная цена парольного мультидевайса. Контрмеры (ADR-013): PBKDF2-HMAC-SHA256 с не менее чем 600 000 итераций, пароль от 12 символов, рекомендация парольной фразы в UI.
**Собеседник.** E2EE не защищает от участника чата: получатель может сохранить, переслать, сфотографировать. «Удалить у всех» после доставки не существует.
**Сам владелец истории.** Привязка экспорта к аккаунту (ADR-014) защищает файл, а не содержимое от владельца. Пользователь, желающий отдать переписку третьему лицу, сделает это скриншотами или модифицированным клиентом — «аналоговая дыра» криптографией не закрывается. К тому же у собеседников есть собственные копии тех же сообщений. Смысл привязки: перемещённый — случайно или злонамеренно — файл `.bare` бесполезен для посторонних.
## Осознанные пределы v1
**Forward secrecy отсутствует.** Компрометация приватного ключа пользователя раскрывает ранее записанные атакующим шифротексты его чатов 1:1 и завёрнутые ключи комнат. Осознанный non-goal v1.
**Вышедший участник до rekey.** После выхода участника сервер перестаёт доставлять ему сообщения, а новый ключ комнаты создаёт владелец при следующем появлении. В промежутке вышедший участник знает действующий ключ; прочитать новые сообщения он может только в сговоре с сервером.
**Push-транспорт идёт через инфраструктуру вендоров браузеров** (FCM, APNs, Mozilla). Это свойство стандарта Web Push, а не наша зависимость. Вендоры видят факт и время доставки пуша.
**Сервер сам ходит по адресу, который выбрал браузер получателя.** Адрес push-сервиса приходит в подписке от клиента, и на каждое сообщение сервер открывает к нему исходящее соединение. Белого списка вендоров нет и не будет: адреса вендоров меняются, а подписку выдаёт браузер. Ограничения — ADR-047: только `https`, только публичные адреса (проверяется уже разрешённый адрес соединения), без следования за редиректами, адрес подписки в журнал не пишется. Остаток риска принят: аутентифицированный пользователь может заставить сервер обратиться к произвольному публичному адресу — до четырёх POST на аккаунт-получателя (доля аккаунта в отправке, ADR-048), то есть до 4×N на сообщение в комнату из N участников, в пределах общих лимитов и восьми отправщиков.