🔐 Менеджер паролей - зачем мы его сделали и как он устроен
Когда вы доверяете свои пароли внешнему сервису, главный вопрос — можно ли ему доверять. Поэтому мы решили открыто рассказать, как изнутри устроен Менеджер паролей 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 чтобы можно было получать пароли в консоли.
В целом продукт ещё в стадии Бета и мы активно собираем обратную связь.
Пожалуй тут и закончим, с удовольствием отвечу на вопросы в комментариях 🤗.
Оставьте отзыв
Если вам нравится пользоваться Tuna, или наоборот вы недовольны чем либо, то пожалуйста оставьте отзыв.
Помощь
Мы ценим наших пользователей и детально изучаем все обращения, если у вас возникли проблемы с tuna – обязательно свяжитесь с нами одним из способов: