Свой сервис для хранения паролей команды — без доверия чужим облакам, с надёжным шифрованием и безопасным обменом доступами.
Пароли и доступы команды хранились где придётся — в заметках, текстовых файлах и переписках, и постоянно терялись. Готовые менеджеры паролей не подходили: либо облако и доверие чужим серверам, либо self-hosted решения с интерфейсом не под наш процесс. Нужен был свой инструмент — со своими пользователями, ролями и безопасным шарингом доступов.
Выбрал AES-256-GCM: конфиденциальность плюс проверка целостности через authentication tag, что позволяет обнаружить подмену зашифрованных данных. Каждая запись шифруется собственным случайным IV, поэтому одинаковые пароли не превращаются в одинаковые шифротексты.
Ключ шифрования нигде не хранится в явном виде — выводится на лету через PBKDF2-SHA512 с 310 000 итераций из мастер-пароля пользователя, серверного "перца" (pepper) и секрета из отдельного ключевого файла. Кражи одного-двух из трёх секретов недостаточно для офлайн-подбора: без ключевого файла даже похищенная база и серверные настройки бесполезны для атакующего.
При усилении схемы (добавлении ключевого файла) старые данные остались зашифрованы по прежней схеме. Полная переиндексация базы "на живую" была рискованной, поэтому сделал миграцию: старая деривация осталась в коде только как одноразовый путь пересчёта, новые записи используют исключительно усиленную схему.
Личные пароли шифруются ключом, привязанным к конкретному пользователю, — сервер не может прочитать их напрямую. Для расшаренных копий сделал отдельный механизм, в котором сервер способен их расшифровать — осознанный компромисс между удобством шаринга и уровнем защиты. Ключевой файл вынесен отдельно от сервера и БД, чтобы доступ к приложению сам по себе не давал возможности расшифровать данные.
Vault хранит пароли команды в PostgreSQL, зашифрованными AES-256-GCM с ключом, который выводится из трёх независимых секретов и нигде не хранится целиком. Личные записи расшифровывает только владелец, расшаренные — по отдельному защищённому механизму.