Files
Luma/Docs/SECURITY.md
T
wt d4b4c92759
iOS CI / Build and Test SwiftUI App (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build Unsigned iOS IPA (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build macOS ZIP (push) Canceled after 0s
tls sasl
2026-09-02 11:46:11 +07:00

118 lines
12 KiB
Markdown
Raw 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.
# Безопасность и модель угроз MVP
- Пароль XMPP сохраняется в Keychain и не попадает в `UserDefaults` или
локальное хранилище истории.
- Проверка сертификата не отключается. Ручной host меняет маршрут подключения,
но XMPP domain/JID остаётся исходной идентичностью.
- TLS trust оценивается системным Apple Security framework как сертификат
сервера (`SecPolicyCreateSSL(true, ...)`) для домена из JID; hostname и цепочка
доверия обязательны даже при ручном адресе подключения.
- Приложение можно заблокировать паролем (хранится в Keychain, выключено по
умолчанию): блокировка срабатывает при запуске и при уходе в фон, а
разблокировка возможна по Face ID/Touch ID.
- Пароль для SCRAM нормализуется по RFC 4013 (SASLprep), чтобы совпадать с
серверной нормализацией; для PLAIN пароль отправляется как введён.
- SCRAM доступен в вариантах SHA-512, SHA-256 и SHA-1 (SHA-512 —
реализация Luma), а также в PLUS-вариантах с канальной привязкой
(SCRAM-SHA-512-PLUS > SHA-256-PLUS > SHA-1-PLUS): gs2-заголовок
«p=tls-exporter,» (RFC 9266) или «p=tls-server-end-point,» доказывает,
что SASL-обмен идёт по тому же TLS-каналу, который видит клиент, и
ломает MITM-ретрансляцию. PLUS выбирается только когда сервер
рекламирует соответствующий channel-binding тип, иначе — обычный SCRAM;
механизм выбирается по рекламе сервера.
- OMEMO работает в legacy-неймспейсе `eu.siacs.conversations.axolotl` (так
умеет закреплённая версия MartinOMEMO) и в OMEMO 2 (`urn:xmpp:omemo:2`,
payload AES-256-CBC + HMAC-SHA-256 в конверте SCE `urn:xmpp:sce:0`). Luma
публикует omemo:2 bundle и список устройств, делит Double Ratchet-сессии с
legacy-модулем и помечает протокол каждого ключа на экране шифрования.
- Канал шифруется STARTTLS/direct TLS собственным TLS-процессором Luma на
OpenSSL 3 (SecureTransport ограничивает потоковый TLS версией 1.2 и не
имеет keying exporter): минимум TLS 1.2, максимум TLS 1.3, ALPN
«xmpp-client», SNI по домену XMPP. Сертификат проверяется через
SecTrust с серверной политикой домена из JID (та же логика
CertificateTrustEvaluator). Фактическая версия TLS, шифр и выбранный
SASL-механизм (самый стойкий из рекламируемых сервером, с канальной
привязкой при её доступности) показываются на экране информации о
сервере.
- Когда OMEMO включено глобально или для конкретного чата, исходящие сообщения
не откатываются на plaintext при ошибке: ошибка показывается пользователю.
- Пользователь может осознанно отключить OMEMO глобально или для отдельного
чата. В этом режиме сервер, MAM-хранилище и участники TLS-терминации видят текст.
- OMEMO доступно и для XEP-0045, но только в неанонимной комнате: клиенту нужны
реальные bare JID участников, чтобы зашифровать ключ для каждого устройства и
расшифровать входящее сообщение от правильной Signal identity. Luma проверяет
режим комнаты, собирает online occupants и доступные списки member/admin/owner,
а при скрытом JID или отсутствии устройств отменяет отправку без plaintext
fallback. Новые комнаты Luma создаются members-only и persistent с
`muc#roomconfig_whois=anyone`.
- Первый увиденный ключ получает доверие TOFU. Изменившийся ключ становится
undecided и требует ручной проверки отпечатка.
- При включённом OMEMO вложения шифруются до загрузки, а URL с ключом находится
внутри OMEMO payload. При выключенном OMEMO файл хранится на HTTP Upload
сервисе в исходном виде; ссылка и метаданные также не имеют E2EE.
- Даже для зашифрованного blob HTTP Upload сервис видит размер и транспортное
имя файла; у voice/video-note это имя содержит тип и округлённую длительность.
- Клиент принимает только HTTPS `put`/`get` URL от XEP-0363 компонента и не
понижает транспорт загрузки до обычного HTTP.
- В OMEMO-исправлении новое тело зашифровано, но стандартный XEP-0308
`<replace>` остаётся метаданными stanza: сервер видит id исправляемого
сообщения, но не новый текст.
- Ответ содержит id исходного сообщения и JID его автора. В зависимости от
поведения OMEMO-библиотеки и клиента эти XEP-0461 метаданные могут быть видны
серверу, даже когда текст ответа зашифрован.
- XEP-0424 не гарантирует стирание: retract может быть проигнорирован сервером
или клиентом, а получатель мог сохранить исходный текст. Fallback retraction
намеренно общий и никогда не повторяет удаляемое содержимое.
- Для inline-preview расшифрованные фото, видео, музыка и голосовые временно
записываются в sandbox `tmp`; Luma очищает известные preview-файлы при смене
аккаунта, а ОС может удалить их раньше как временные данные.
- Выбранные для пакетной отправки файлы временно копируются в `tmp` для экрана
предпросмотра и удаляются при отмене либо после завершения отправки.
- Core Location вызывается только из экрана явной отправки точки. Выбранные
координаты становятся содержимым сообщения и защищены OMEMO лишь тогда, когда
шифрование включено для этого чата; при plaintext сервер и MAM видят точку.
- Аудио/видео звонка шифруются WebRTC DTLS-SRTP между конечными клиентами.
Это не OMEMO: XMPP-сервер видит Jingle-сигналинг и адрес собеседника, а
STUN/TURN-сервис видит сетевые адреса и объём трафика. TURN ретранслирует уже
зашифрованный медиапоток. Если Prosody не публикует собственный сервис через
XEP-0215, резервный публичный STUN получает запрос на определение адреса.
- На iOS файл OMEMO-состояния и база локальной истории (SwiftData
`.store`) получают data-protection `completeUntilFirstUserAuthentication`.
- Архивные эхо собственных служебных станз (receipts, chat markers,
повторно доставленные копии) и собственные сообщения, которые невозможно
расшифровать обратно (нет encrypt-to-self ключа или self-сессия устарела),
не превращаются в пузыри «Не удалось расшифровать сообщение»: они
отбрасываются, как это делают другие клиенты, чтобы не засорять историю
фантомами, которых нет в виде сообщений на сервере.
- Сохранение медиа выполняется только по явному действию пользователя:
фото/видео/видеосообщения — в медиатеку, файлы и голосовые — в «Файлы»
(Загрузки). Расшифрованное содержимое при этом покидает sandbox приложения.
## Известные ограничения
- На macOS история (SwiftData SQLite в Application Support) и OMEMO state
лежат в sandbox без дополнительного SQLCipher-слоя; защиту обеспечивает
учётная запись и FileVault пользователя. Для повышенной модели угроз нужна
зашифрованная база.
- Локальная история содержит уже расшифрованные тексты, как и у большинства
клиентов. Полное удаление требует выхода с опцией удаления истории.
- «Удалить у меня» удаляет запись только из локального SwiftData-хранилища
этого устройства и запоминает id против повторной MAM-загрузки; оно не
удаляет серверный архив, другие собственные устройства или копию собеседника.
- Глобальная политика шифрования хранится в `UserDefaults` отдельно для каждого
JID, а переопределение чата — в локальном SwiftData-хранилище. Эти значения
не являются секретами, но прямо влияют на конфиденциальность следующей
отправки.
- Аватары XEP-0084/vCard не шифруются OMEMO. Их видимость определяется
настройками PEP и roster на сервере; не используйте аватар как секретное фото.
- Используемая реализация MartinOMEMO поддерживает распространённый legacy
namespace. Она не должна называться полной реализацией новой редакции
XEP-0384 без отдельного interoperability test suite.
- В открытой или плохо настроенной сторонней MUC списки офлайн-участников могут
быть недоступны. Для предсказуемого группового OMEMO используйте members-only
комнату, разрешающую участникам получать affiliation lists; иначе отключите
OMEMO именно для этой комнаты осознанно.
- Jailbreak/root, скомпрометированный endpoint и запись экрана находятся вне
модели угроз MVP.