Как устроено
Как Lumen держит профили врозь
Для технических читателей: какие процессы запускаются, где лежат ключи, как трафик профиля попадает в прокси и что видит сервер синхронизации.
Общая схема
В Lumen два вида процессов. Менеджер открывает хранилище мастер-паролем и держит список профилей, их ключи, список прокси и настройки этого Mac. Каждый запущенный профиль - отдельный процесс macOS: в нём работают интерфейс Safari и системный WebKit, и он знает только свой профиль.
Между процессом профиля и сетью стоит его прокси, а вокруг процесса - профиль песочницы ядра: без UDP и без доступа к папкам настоящей Safari.
Процесс профиля
Менеджер запускает процесс профиля и передаёт ему ключ этого профиля и его запись из списка - через стандартный ввод. Не в аргументах командной строки, которые видит любой процесс пользователя, не в переменных окружения и не в файле. Хранилище, открытое таким ключом, читает и пишет файлы одного профиля и отказывает во всём остальном: ключи и имена других профилей, список прокси и настройки синхронизации остаются у менеджера.
Окружение процесса собирается по списку, а не наследуется: имя пользователя и оболочка, кодировка, язык приложения, фиксированный PATH и собственные дом, временная папка и часовой пояс профиля. Ничего больше от менеджера в процесс не попадает.
Язык, регион и часовой пояс задаются процессу при запуске - так их получают и WebKit, и страница, и её воркеры. Скрипт поверх страницы для этого не нужен.
Если канал к менеджеру закрылся - менеджер завершился или упал, - процесс профиля запечатывает данные и выходит. Пока процесс работает, он держит блокировку своего дома: менеджер, запущенный после сбоя, запечатает дом только тогда, когда процесса уже нет.
Что задаётся процессу
| Что задаётся процессу | Значение |
|---|---|
HOME, CFFIXED_USER_HOME, TMPDIR | папки внутри дома профиля |
AppleLanguages, AppleLocale | язык и регион страны прокси или выбранные вами |
TZ | часовой пояс места выхода прокси |
| Ключ | ключ одного профиля; ключа хранилища нет |
Ключи и шифрование
Мастер-пароль превращается в ключ шифрования ключей функцией Argon2id (RFC 9106, 64 МиБ, 3 прохода, 4 потока). Этим ключом открывается ключ хранилища - случайные 256 бит. Из него через HKDF получаются ключи для отдельных назначений: списка профилей, синхронизации, настроек устройства, списка прокси. У каждого профиля свой случайный ключ, а из него - ключи паролей, истории, закладок, cookies, прогрева и архива данных сайтов.
Каждый шифртекст AES-256-GCM связан со своим назначением и владельцем через связанные данные - например, history:v1:<id профиля>. Переложить файл в другой профиль или подменить один файл другим нельзя: расшифровка не пройдёт.
Смена мастер-пароля перешифровывает только ключ хранилища. Новое устройство открывает хранилище тем же паролем: заголовок с солью, параметрами Argon2id и зашифрованным ключом хранилища синхронизируется открытым - секретов в нём нет.
Данные сайтов профиля WebKit хранит в своих папках, и API, чтобы шифровать их на лету, у него нет. Поэтому при остановке профиля они архивируются, архив шифруется отдельным ключом, а папки удаляются; при запуске - восстанавливаются. Пока профиль работает, они лежат на диске открытыми под защитой прав доступа macOS и FileVault.
Удалённые файлы на SSD с APFS нельзя надёжно затереть, и мы не делаем вид, что можем. Для удалённых блоков настоящая защита - FileVault.
Сеть и прокси
Прокси профиля задаётся его хранилищу данных WebKit без права обхода: если прокси недоступен, загрузка завершается ошибкой, а не уходит напрямую. Но в процессе профиля работают и фреймворки Safari, поэтому прокси удерживается для всего процесса, а не только для известных нам веб-представлений.
| Канал | Как закрыт | Чем проверяется |
|---|---|---|
| Веб-представления процесса | Каждое получает хранилище профиля с его прокси при создании; любое другое хранилище указывает на прокси, которого нет | Отчёт процесса: каждое веб-представление - с прокси |
| Собственные запросы процесса | Сетевой контекст и все сессии URLSession - с прокси профиля | Три пробных запроса приходят на прокси |
| UDP и QUIC | Песочница ядра запрещает процессу UDP; без неё процесс не запускается | Датаграмма и QUIC-соединение не отправлены |
| Обращения к самому Mac | Блокировка 127.0.0.0/8, localhost, ::1 и 0.0.0.0 для страниц не с этого Mac | Слушатель на loopback не получил ни одного соединения |
| dns-prefetch | Блокировка загрузок типа ping; настройка закреплена | В захвате нет DNS-запросов к именам тестовой страницы |
| Сертификаты | Проверка без загрузки промежуточных сертификатов и OCSP | Сертификат-приманка отклонён, запроса за ним нет |
| WebRTC и WebTransport | Выключены во всех веб-представлениях и воркерах профиля с прокси | Их нет ни в странице, ни во фреймах, ни в воркерах |
| Службы Safari в других процессах | Соединения с ними не устанавливаются | В захвате нет пакетов этих служб |
Синхронизация
Синхронизация переносит между вашими Mac настройки профиля, пароли, закладки, историю и постоянные cookies. Каждый объект шифруется на Mac до отправки (AES-256-GCM), а в связанных данных записаны хранилище, объект и ревизия: сервер не может переложить объект под другое имя или выдать старую ревизию за новую.
Имена объектов на сервере - HMAC от их настоящих имён: сервер не видит даже того, что объект - это «история профиля».
Конфликты разрешаются на устройстве: история и закладки сливаются по записям, cookies - по одному (побеждает более новое изменение), у настроек побеждает более поздняя правка, а другая сохраняется копией.
Сервер знает
- Идентификатор хранилища
- Сколько объектов и какого размера
- Счётчики ревизий и время изменений
- Сколько профилей на каждом Mac - приложение сообщает число, чтобы лимит тарифа был общим на все ваши Mac
Сервер не знает
- Имена профилей, их теги и заметки
- Настройки и адреса прокси
- Пароли, cookies, историю, закладки, адреса сайтов
Границы
То, что Lumen не делает и не обещает, - так же важно, как то, что делает.
Видеокарта, экран, системные шрифты и звук у всех профилей на одном Mac одинаковы, как у вкладок одной Safari. Профили различаются хранилищем, сетью, языком и часовым поясом.
Ключи доступа проверяют службы системы из iCloud Keychain владельца, а WebTransport идёт по UDP, которого прокси не переносит. Поэтому в профиле с прокси этих интерфейсов нет - и по этому страница может отличить его от Safari.
WebKit не пускает WebRTC через прокси. Поэтому в профиле с прокси он выключен, а не идёт напрямую: видеозвонки в браузере там не работают.
Пока профиль запущен, данные его сайтов лежат в папках WebKit в открытом виде. Их защищают права доступа macOS и FileVault.
Мы не храним ни пароль, ни ключи. Забытый пароль - потерянные данные, и это цена того, что прочитать их можете только вы.
Проверьте наши утверждения
Что именно сравнивается с Safari и как ловятся утечки - с цифрами последних прогонов.