🔐 Менеджер паролей - зачем мы его сделали и как он устроен
Когда вы доверяете свои пароли внешнему сервису, главный вопрос — можно ли ему доверять. Поэтому мы решили открыто рассказать, как изнутри устроен Менеджер паролей Tuna: какими ключами и в каком порядке шифруются данные, что хранится у нас на сервере, а что — только у вас, и что пр оизойдёт, если наша база данных утечёт. Цель простая — чтобы пользоваться менеджером было не страшно, потому что вы понимаете, как он работает.
Все ключи шифрования создаются на вашем устройстве и никогда не покидают клиент в открытом виде. На сервер уходит только зашифрованный текст, а Мастер-ключ есть только у вас. Это модель нулевого знания (zero-knowledge): даже если наша инфраструктура будет скомпрометирована и утечёт вся база данных — расшифровать ваши пароли без Мастер-ключа невозможно.
Нафига мы сделали менеджер паролей?
Мы много работали по фрилансу в разных компаниях, поэтому есть насмотренность как люди меняются корпоративными паролями к сервисам, инфраструктуре и прочему. Ну так вот, если в компании нет купленного 1Password или чего-то подобного (что там Дудь рекламирует), то это просто аншлаг.
- Нет единого хранилища, чаще всего это просто какой-то 1 ответственный человек, если он уйдёт, то всё...
- Пароли передают в чатиках Tg или Slack
- При увольнении сотрудника, если есть какая то общая учётка от внешнего сервиса, приходится почти рассылать новый пароль всем.
Там ещё скоуп проблем, но это основное.
В общем посмотрели мы на это всё и подумали, что у нас 99% пользователей это разработчики, наверняка у них та же самая проблема. Есть потребность хранить какие то инфраструктурные пароли, SSH ключи, офисный WI-FI и так далее. Но не всегда есть доступные инструменты для безопасного и удобного способа делиться ими с командой, заказчиками, аудиторами или просто друзьями.
Так вот и решили, что надо — ну и сделали.
Сколько стоит?
Мы решили, что это будет базовым бесплатным функционалом в рамках нашей платформы.
Безопасность — это базовая потребность (даже по пирамиде Маслоу), и мы хотим, чтобы даже самые маленькие стартапы на 5 человек могли себе позволить безопасно хранить и обмениваться паролями в компании или друзьями.
Возможно позже мы введём какие то разумные ограничения или новые возможности которые будут доступны только с подпиской, но пока весь функционал доступен бесплатно всем. Вы можете зарегистрироваться, создать Команду, пригласить пользователей и завести общие каталоги с паролями без оформления подписки.
Архитектура
Что ж, ничего сложного, генерируем на клиенте какой-то ключ показываем его пользователю, нигде не сохраняем, этим ключом пользователь на клиенте шифрует пароли, в зашифрованном виде передаёт на сервер, мы храним. Когда пользователь хочет прочитать пароль, отдаём его, пользователь расшифровывает этот пароль своим ключом который есть только у него — профит.
Кажется всё просто, но сложность начинается когда нам надо не только хранить пароли одного пользователя, а иметь какие то общие каталоги в командах и делиться паролями наружу одноразовой ссылкой. Тут то мы и начинаем замешивать симметричные и асимметричные ключи, шифровать одно другим и вообще становится не так всё просто.
Общие каталоги в команде
Вообще в этом параграфе мы разберём почти полное устройство, так как будем распутывать клубок из конца в начало.
Начнём с задачи — иметь общие каталоги с паролями в команде. Тут есть 2 пути:
- Пароли должны быть зашифрованы 1 ключом который есть у всех.
- При добавлении нового пользователя мы дублируем все п ароли и шифруем его публичным ключом.
Второй поход нам не подходит, так как пользователь 1 должен иметь возможность редактировать пароль, а пользователь 2 должен сразу это видеть и наоборот. Значит — пароли должны быть зашифрованы одним ключом который есть у всех. Как такое провернуть? А вот так:
В итоге пароли шифруются симметричным AES ключом, назовём его — AES-ключ каталога. Но не храним же мы его в открытом виде? Верно, этот AES-ключ каталога на самом деле зашифрован RSA-ключом пользователя.
Распутываем клубок ...
Когда вы инициируете менеджер паролей, для вашего пользователя генерируется ассиметричный RSA ключ, его приватная часть шифруется Мастер-ключом, а публичная сохраняетися в открытом виде.
Итого, пользователь хранит:
- свой Мастер-ключ
Итого, мы храним:
- публичный RSA-ключ
- зашифрованный приватный RSA-ключ
- зашифрованный AES-ключ каталога
- зашифрованный пароли
Давайте попробуем посмотреть картину в целом и собрать в кучу весь алгоритм что там вообще происходит?
Если коротко, то основная концепция в том, что на стороне сервера если что-то и хранится, то исключительно в зашифрованном виде, а на клиенте в открытом виде всё может существовать только в Runtime и перманентно тоже нигде не хранится.
Чтобы проще это понять нужно пройтись по шагам:
- Пользователь инициирует менеджер паролей
- На клиенте (браузер) генерируется пара RSA-ключей приватный и публичный.
- На клиенте генерируется Мастер-ключ, пользователь должен сохранить его себе
- На клиенте приватный RSA-ключ шифруется Мастер-ключом
- На сервер передаётся зашифрованный приватный RSA-ключ и публичный RSA-ключ
- Пользователь создаёт новый каталог
- На клиенте генерируется AES-ключ каталога
- На клиенте AES-ключ катал ога шифруется публичным RSA-ключом
- На клиенте пароли шифруются AES-ключом каталога
Когда нам надо сделать пароли общими, мы находим пользователя в команде, берём его публичный RSA-ключ, берём AES-ключ каталога расшифровываем его своим приватным RSA-ключом, шифруем публичным RSA-ключом нового пользователя и отправляем на сервер, на сервере у каталога появляется новый пользователь с AES-ключом каталога и новый пользователь теперь может расшифровать его своим приватным RSA-ключом, а затем и пароли зашифрованные AES-ключом каталога.
В итоге получается примерно вот такая колбаса:
Фух ...
Делимся паролями наружу
Если с пользователями внутри команды разобрались, то как делиться паролями наружу, ведь у незарегистрированных пользователей нет RSA-ключей и они вообще ничего не знают?
Тут мы приходим к тому нужно сделать копию пароля. Снова генерируем AES-ключ но в этот раз он будет только для 1 пароля. Зашифрованному паролю присваиваем UID и отправляем это на сервер.
Что знает клиент:
- UID пароля
- AES-ключ которым он зашифрован
Что знает сервер:
- UID пароля
- Зашифрованный пароль
Далее клиент генерирует ссылку, где часть URI — это UID пароля, а другая AES-ключ:
https://my.tuna.am/password#{UID}#{AES-ключ}
Соответственно у себя в базе мы не храним AES-ключ и не можем расшифровать пароль, но пользователь, что перейдёт по ссылке без труда его расшифрует и посмотрит.
Да, формально мы можем расшифровать пароль в момент 1-го же обращения, так как в запросе мы увидим полную ссылку, но делать этого мы конечно не будем. В целом есть ид еи как улучшить этот момент, но пока всё на этапе обсуждения. Тем не менее именно в базе данных всё зашифровано.
Думаю тут можно завершить с архитектурой и перейти дальше.
Какие планы?
Мы уже получили обратную связь от CTO одного из наших клиентов, с просьбой добавить поддержку TOTP (Time-based one-time Password), думаю добавим в ближайшем релизе. Это актуально если в компании есть какой-то сервис с одним аккаунтом и вход там с подтверждением через TOTP.
Думаем как сделать функционал Делимся паролями наружу более безопасным, но при этом таким же удобным.
Также есть планы внедрить работу с паролями в консольную утилиту tuna чтобы можно было получать пароли в консоли.
В целом продукт ещё в стадии Бета и мы активно собираем обратную связь.
Пожалуй тут и закончим, с удовольствием отвечу на вопросы в комментариях 🤗.
Leave feedback
If you enjoy using Tuna, or on the contrary you are not happy with something, please leave feedback.
Help
We value our users and carefully review every request. If you have any problems with tuna, please contact us in one of the following ways: