Лимиты: все четыре правила 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
24 lines
3.8 KiB
Markdown
24 lines
3.8 KiB
Markdown
# ADR-062: `GET /api/kdf` не обещает скрывать существование ника
|
|
|
|
Уточняет [ADR-015](015-password-never-leaves-client.md): «ответ не раскрывает существование ника» верно не всегда.
|
|
|
|
## Контекст
|
|
|
|
`GET /api/kdf?nick=` отдаёт число итераций PBKDF2: для известного ника — `iter` из его ключевого блоба, для неизвестного — целевое значение сервера. ADR-015 и `docs/protocol.md` называли это свойство прямо: ответ не раскрывает, существует ли ник.
|
|
|
|
Утверждение держится ровно до первого повышения цели. ADR-013 и ADR-030 предусматривают повышение с автоматической перешифровкой блоба при следующем входе: у аккаунтов, заведённых раньше и с тех пор не входивших, в блобе остаётся прежнее число. Тогда известный ник отвечает старым значением, неизвестный — новым, и разница видна снаружи. Сейчас цель не менялась, поэтому оракул спящий, — но он следует прямо из документированного пути обновления, а не из ошибки.
|
|
|
|
Прятать существование ника Bare и не обещал в остальном: ADR-019 говорит про ник открытым текстом — «что он существует, узнать можно. Это не считается утечкой», а `POST /api/register` отвечает `409 nick_taken`. Ради согласованности одной строки нет смысла ни отдавать всем целевое значение (клиенту нужно настоящее — иначе не расшифровать блоб), ни заводить второй запрос за `iter` после входа.
|
|
|
|
## Решение
|
|
|
|
- Формулировка правится: `GET /api/kdf` отвечает `200` и неизвестному нику, поэтому по статусу существование ника не видно; но число итераций у существующего аккаунта — его собственное, и после повышения цели оно может отличаться от целевого. Скрытием существования ника этот ответ не занимается.
|
|
- Существование ника остаётся публичным фактом (ADR-019), и это записано там же, где раньше стояло обещание: `docs/protocol.md`, «Публичные».
|
|
- Код не меняется: разное число итераций у разных аккаунтов — свойство постепенного повышения (ADR-013), а не дефект.
|
|
|
|
## Следствия
|
|
|
|
- Перечень того, что видно снаружи без сессии, становится честным: существование ника видно и через регистрацию, и через `kdf`.
|
|
- Повышение цели KDF остаётся возможным без миграции всех аккаунтов разом — ровно ради этого `iter` и лежит рядом с блобом.
|
|
- Если скрывать существование ника когда-нибудь понадобится, это отдельное решение и другой протокол входа; в v1 такой задачи нет.
|