Как мы создавали менеджер паролей со стойкой криптографией и мастер-паролем. Опыт команды Яндекс.Браузера
Как ни странно, но только 1% пользователей браузера используют специализированные расширения для хранения паролей (LastPass, KeePass, 1Password, …). Безопасность паролей всех остальных пользователей зависит от браузера. Cегодня мы расскажем читателям Хабрахабра, почему наша команда отказалась от архитектуры защиты паролей из проекта Chromium и как разработала собственный менеджер паролей, который уже тестируется в бете. Вы также узнаете, как мы решили проблему сброса мастер-пароля без расшифровки самих паролей.
С точки зрения безопасности, на каждом сайта рекомендуется использовать свой уникальный пароль. Если злоумышленники украдут один пароль, то только к одному сайту они и получат доступ. Проблема в том, что запомнить десятки надёжных паролей очень сложно. Кто-то честно придумывает новые пароли и записывает их руками в блокнот (а потом теряет вместе с ним же), другие — используют один и тот же пароль на всех сайтах. Трудно сказать, какой из этих вариантов хуже. Решением проблемы для миллионов обычных пользователей может быть встроенный в браузер менеджер паролей, но его эффективность зависит от того, насколько он прост и надёжен. И в этих вопросах у предыдущего решения были пробелы, о которых мы и расскажем ниже.
Почему мы создаем новый менеджер паролей?
В текущей реализации менеджера паролей для Windows, унаследованной из Chromium, сохранённые пароли защищены браузером достаточно просто. Они зашифрованы средствами операционной системы (к примеру, на Windows 7 используется функция CryptProtectData, основанная на алгоритме AES), но хранятся не в изолированной области, а просто в папке профиля. Казалось бы, в этом нет проблемы, ведь данные зашифрованы, но ключ для расшифровки тоже хранится в операционной системе. Любая программа на компьютере может перейти в папку профиля браузера, взять ключ, локально расшифровать пароли, отправить их на сторонний сервер, и никто этого не заметит.
А ещё многие пользователи хотели бы, чтобы случайный человек, не обладающий специальной подготовкой, но получивший кратковременный доступ к браузеру (например, родственник или коллега по работе), не смог авторизоваться на важных сайтах с помощью сохранённых паролей.
Обе эти проблемы решаются с помощью мастер-пароля, которым защищаются данные, но который нигде не хранится. И это стало нашим первым требованием к новой архитектуре хранения паролей в Яндекс.Браузере. Но не единственным.
Каким бы безопасным ни был новый менеджер паролей, его популярность зависит от того, насколько просто им пользоваться. Напомним, что те же 1Password, KeePass и LastPass даже в сумме используют не более процента пользователей (хотя LastPass мы предлагаем в нашем встроенном каталоге дополнений). Или другой пример. Вот так в старой реализации Браузер предлагает сохранить пароль:
Опытные пользователи или согласятся, или откажутся, или сделают хоть что-то с этим уведомлением. Но в 80% случаев его просто не замечают. Многие пользователи даже не знают, что в браузере можно сохранять пароли.
Отдельно стоит сказать про функциональность. Сейчас даже добраться до списка своих паролей не так уж и просто. Нужно открыть меню, кликнуть по настройкам, перейти в дополнительные настройки, найти там кнопку управления паролями. И только тогда человек получит доступ к примитивному списку аккаунтов, которые нельзя отсортировать по логину, нельзя добавить текстовое примечание, отредактировать тоже нельзя. К тому же менеджер паролей должен помогать придумывать новые пароли.
И ещё кое-что. Для нас было важно, чтобы новая архитектура соответствовала принципу Керкгоффса, то есть, чтобы её надежность не зависела от знаний злоумышленников о применяемых алгоритмах. Криптосистема должна оставаться безопасной даже в том случае, когда им известно всё, кроме применяемых ключей.
Почему мы не взяли готовое решение?
Существуют продукты с открытым исходным кодом, которые поддерживают мастер-пароль и расширенную функциональность. Их можно было бы интегрировать в браузер, но они нам не подошли по ряду причин.
В первую очередь на ум приходит KeePass. Но его хранилище зашифровано целиком, а у нас в Браузере синхронизация работает построчно. А значит, надо либо спрашивать мастер-пароль при каждой синхронизации, либо шифровать записи раздельно. Второй вариант добрее к пользователям. Более того, для массового продукта важно, чтобы пользователь знал о возможности подставить сохранённый пароль до разблокировки базы мастер-паролем, поэтому часть информации должна оставаться незашифрованной.
У специализированных дополнений для работы с паролями есть возможность сбросить мастер-пароль, если пользователь его забыл. Но для этого нужно скачать, спрятать и не потерять резервный код или файл. Это нормально, когда речь идет об опытных пользователях, но это сложно для всех остальных. Поэтому нам нужно было придумать альтернативное решение. Спойлер: в итоге нам удалось найти решение, при котором мастер-пароль сбросить можно, но даже Яндекс не сможет получить доступ к базе. Но об этом чуть позже.
А ещё любое стороннее решение в любом случае пришлось бы серьезно дорабатывать, чтобы нативно интегрировать в браузер (переписать на C++ и Java) и сделать его достаточно простым для пользователей (полностью заменить весь интерфейс). Как бы удивительно это ни звучало, но написать новую архитектуру хранения и шифрования паролей проще, чем сделать всё остальное. Поэтому логичнее не пытаться связать два изначально несовместимых продукта в один, а доработать свой.
Новая архитектура с использованием мастер-пароля
В хранении самих записей нет ничего необычного. Мы используем надежный и быстрый алгоритм AES-256-GCM для шифрования паролей и примечаний, адреса и логины не шифруем для удобства применения, но подписываем для защиты от подмены. Похожим образом устроена схема хранилища в том же 1Password.
Самое интересное — это защита 256-битного ключа encKey, который необходим для расшифровки паролей. Это ключевой момент безопасности паролей. Если злоумышленник узнает этот ключ, то легко взломает всё хранилище независимо от сложности алгоритма шифрования. Поэтому защита ключа основана на следующих базовых принципах:
— Доступ к ключу шифрования блокируется мастер-паролем, который нигде не хранится.
— Ключ шифрования не должен быть математически связан с мастер-паролем.
В простых сервисах и приложениях ключ шифрования получают путем хэширования мастер-пароля, чтобы хоть так замедлить атаку перебором. Но математическая зависимость ключа от мастер-пароля всё же упрощает взлом, скорость которого в этом случае зависит лишь от надежности хэширования. Применение ферм из заточенных на взлом ASIC-процессоров сейчас уже не редкость. Поэтому в нашем случае ключ encKey не является производным от мастер-пароля и генерируется случайно.
Далее ключ encKey зашифровывается с помощью асимметричного алгоритма RSA-OAEP. Для этого Браузер создает пару ключей: открытый pubKey и закрытый privKey. Ключ encKey защищается с помощью открытого ключа, а расшифровать его можно только с помощью закрытого.
Открытый ключ pubKey защищать не нужно, потому что он не подходит для расшифровки, а вот с закрытым privKey история другая. Чтобы защитить его от кражи, доступ к нему блокируется согласно стандарту PKCS#8 с помощью парольной фразы unlockKey, которая в свою очередь является результатом хэширования мастер-пароля с помощью функции PBKDF2-HMAC-SHA256 (100 тысяч повторов; с добавлением соли и id хранилища). Если мастер-пароль случайно совпадает с уже украденным паролем от какого-либо сайта, добавление соли скроет этот факт и усложнит взлом. А благодаря многократному хэшированию достаточно длинного мастер-пароля трудоемкость взлома unlockKey сопоставима со взломом ключа encKey.
Зашифрованные пароли, зашифрованный ключ к ним encKey, зашифрованный закрытый ключ privKey и открытый ключ pubKey хранятся в профиле браузера и синхронизируются с другими устройствами пользователя.
Чтобы было проще разобраться во всём этом, приведем схему расшифровки паролей:
У подобной архитектуры с использованием мастер-пароля есть ряд преимуществ:
— 256-битный ключ шифрования хранилища генерируется случайно и обладает высокой криптостойкостью по сравнению с паролями, придуманными человеком.
— При брутфорсе мастер-пароля злоумышленник не узнает результат, если не пройдется по всей цепи (пароль-PBKDF2-RSA-AES). Это очень долго и очень дорого.
— Если функция хэширования будет скомпрометирована, мы в любой момент можем перейти на альтернативный вариант хэширования с сохранением обратной совместимости.
— Если злоумышленник узнает мастер-пароль, то сменить его можно без сложной и рискованной процедуры расшифровки всего хранилища, потому что ключ шифрования данных не связан с мастер-паролем, а значит, не скомпрометирован.
— Ключ шифрования хранится в зашифрованном виде. Ни Яндекс, ни злоумышленник, похитивший пароль от Яндекса, не смогут получить доступ к синхронизированным паролям, поскольку для этого нужен мастер-пароль, который нигде не хранится.
Но у варианта с мастер-паролем есть один «недостаток»: пользователь может забыть мастер-пароль. Это нормально, когда речь идет о специализированных решениях, которые используют опытные пользователи, хорошо осознающие риск. Но в продукте с многомиллионной аудиторией это неприемлемо. Если мы не предусмотрим резервный вариант, то многие пользователи Яндекс.Браузера либо откажутся от использования мастер-пароля, либо «потеряют» однажды все свои пароли, а виноват в этом будет Браузер (вы удивитесь, но именно Яндекс часто оказывается крайним в ситуации, когда человек забыл пароль от аккаунта). И придумать решение не так уж и просто.
Как сбросить мастер-пароль без раскрытия паролей?
В некоторых продуктах эта проблема решается с помощью хранения расшифрованных данных (или даже мастер-пароля) в облаке. Этот вариант для нас не подходил, потому что злоумышленник может украсть пароль от Яндекса, а вместе с ним и пароли от всех сайтов. Поэтому нам нужно было придумать такой способ восстановления доступа к хранилищу паролей, при котором никто, кроме самого пользователя, не смог бы это сделать. Сторонние менеджеры паролей предлагают для этого создать резервный файл, который пользователь должен самостоятельно хранить в надежном месте. Хорошее решение, но обычные пользователи такие резервные ключи будут неизбежно терять, поэтому у нас всё намного проще.
Ещё раз вспомним цепочку зависимостей ключей. Хранилище паролей зашифровано с помощью случайного ключа encKey, который нигде не хранится в явном виде. Этот ключ защищён с помощью закрытого ключа privKey, который также не хранится в явном виде и в свою очередь защищён с помощью сложного хэша от мастер-пароля. Когда человек забывает мастер-пароль, он фактически лишается возможности расшифровать ключ privKey. Это значит, что в качестве резервного варианта можно хранить дубликат ключа privKey. Но где? И как его защитить?
Если поместить расшифрованный privKey в облако, то безопасность паролей будет зависеть от аккаунта Яндекса. А ровно этого мы и не хотели допускать. Если же хранить его в явном виде локально, то вся защита с мастер-паролем теряет какой-либо смысл. Нет такого места, где можно было бы безопасно хранить этот ключ в явном виде. Значит, его надо шифровать. Для этого Браузер создает случайный 256-битный ключ, которым защищает дубликат privKey. Теперь самое интересное. Этот случайный ключ отправляется на хранение в облако Яндекс.Паспорта. А зашифрованный дубликат остается храниться в локальном профиле Браузера. Получается, что ни в облаке, ни на компьютере нет готовой пары для расшифровки паролей, и безопасность не страдает.
При таком варианте сбросить мастер-пароль можно было бы только там, где и создан дубликат ключа privKey. Мы же хотели добавить такую возможность и синхронизированным устройствам. Создавать резервный ключ на каждом устройстве вручную неудобно: можно случайно остаться с тем устройством на руках, на котором забыли создать дубликат. Отправлять зашифрованный дубликат на другие устройства с помощью синхронизации нельзя: в облаке уже хранится ключ к нему, и в целях безопасности им нельзя встречаться в одном месте. Поэтому зашифрованный дубликат privKey проходит через ещё один слой шифрования. В этот раз — с помощью хэша от мастер-пароля. Мастер-пароль не хранится в облаке, поэтому полученную «матрешку» уже можно смело синхронизировать. На других устройствах в момент первого ввода мастер-пароля дополнительный слой шифрования будет снят.
В итоге, когда пользователь забудет мастер-пароль, ему будет достаточно запросить сброс пароля через браузер и подтвердить свою личность с помощью пароля от Яндекса.
Браузер запросит ключ у Яндекс.Паспорта, расшифрует им дубликат ключа privKey, с его помощью расшифрует ключ от хранилища encKey, а дальше создаст новую пару pubKey и privKey, последний из которых будет защищён новым мастер-паролем. Хранилище паролей при этом не расшифровывается, что снижает риск потери данных. К слову, принудительно сменить encKey и перешифровать данные тоже можно: достаточно отключить и заново включить мастер-пароль в настройках.
Получается, что сбросить мастер-пароль сможет только сам пользователь и только на том устройстве, где он хотя бы раз его вводил. Конечно же, резервный ключ создавать не обязательно, если пользователь уверен в себе. Даже мастер-пароль можно не использовать, хотя мы и не рекомендуем от него отказываться.
Новая архитектура и мастер-пароль — не единственные изменения в новом менеджере. Как мы уже рассказывали выше, удобство в использовании и расширенные возможности важны не меньше.
Новый менеджер паролей
Прежде всего, мы отказались от незаметной серой панели с предложением сохранить пароль. Теперь пользователь увидит предложение рядом с полем пароля. Не заметить такое уже трудно.
Да и сам менеджер теперь не надо искать в настройках: кнопка доступна в главном меню. Список сохранённых аккаунтов теперь поддерживает сортировку по логину, адресу и примечанию. Мы также добавили редактирование записей.
Подсказка: примечания отлично подходят в качестве альтернативы меткам, потому что поддерживают поиск.
А ещё Браузер теперь помогает создавать уникальные пароли.
В первой бета-версии мы успели далеко не всё. В будущем мы поддержим экспорт и импорт паролей для совместимости с популярными сторонними решениями. Также у нас есть идея добавить настройки генератору паролей.
Мобильный менеджер паролей
Конечно же, новая логика и поддержка мастер-пароля появятся не только на компьютере, но и в версиях Яндекс.Браузера для Android и iOS. С небольшой адаптацией. К примеру, можно использовать не только мастер-пароль, но и отпечаток пальца. Мы также запретили программно делать скриншоты на странице со списком паролей — можно не бояться вредоносных приложений.
Сегодня новый менеджер паролей можно попробовать в бета-версии Яндекс.Браузера для Windows и macOS (версия для Linux традиционно собирается на базе стабильного кода, поэтому выйдет чуть позже). В ближайшее время он также заработает в альфа-версии Браузера для Android (а ещё через некоторое время появится и в бете для iOS).
Мы постоянно ищем баланс между простым, но надежным инструментом для миллионов пользователей и расширенными возможностями для тех, кому они нужны. Пожалуйста, поделитесь с нами видением идеального менеджера паролей, который именно вы хотели бы видеть в браузере.
И ещё кое-что. Мы приглашаем специалистов в области безопасности помочь нам найти уязвимости в новом менеджере паролей в рамках программы «Охота за ошибками». С вашей помощью менеджер паролей станет ещё безопаснее. Спасибо!