Безопасность PasswordVault при использовании из приложения Desktop

Я хотел бы использовать https://docs.microsoft.com/en-us/uwp/api/windows.security.credentials.passwordvault в своем настольном приложении (на основе WPF) для безопасного хранения пароля пользователя. Мне удалось получить доступ к этому Windows 10 API с помощью этой статьи MSDN.

Я провел несколько экспериментов, и оказалось, что любые данные, записанные в PasswordVault из одного настольного приложения (а не из собственного приложения UWP), можно прочитать из любого другого настольного приложения. Даже упаковка моего настольного приложения с технологией Desktop Bridge и, следовательно, наличие идентификатора пакета не устраняет эту уязвимость.

Любые идеи, как это исправить и сохранить данные приложения в безопасности от других приложений?

ОБНОВЛЕНИЕ: оказалось, что PasswordVault не добавляет дополнительной защиты по DPAPI. Дело закрыто с отрицательным результатом.

5 ответов

Решение

(это из того, что я могу понять из вашего поста)

Нет реального способа предотвратить доступ к данным между настольными приложениями, если использовать этот вид API. http://www.hanselman.com/blog/SavingAndRetrievingBrowserAndOtherPasswords.aspx рассказывает об этом подробнее. Вы, вероятно, просто хотите расшифровать свою информацию.

Ограничение доступа к памяти затруднено, код, выполняемый пользователем, всегда доступен для извлечения пользователем, поэтому было бы трудно ограничить это.

Рассматривали ли вы использование API защиты данных Windows: https://msdn.microsoft.com/en-us/library/ms995355.aspx

Извлеченный прямо из источникаDPAPI - это простая в использовании служба, которая будет полезна разработчикам, которые должны обеспечивать защиту конфиденциальных данных приложения, таких как пароли и закрытые ключи.

WDPAPI использует ключи, сгенерированные операционной системой, и Triple DES для шифрования / дешифрования ваших данных. Это означает, что ваше приложение не должно генерировать эти ключи, что всегда хорошо.

Вы также можете использовать класс Rfc2898DeriveBytes, который использует генератор псевдослучайных чисел для расшифровки вашего пароля. Это безопаснее, чем большинство расшифровщиков, поскольку нет практического способа вернуться от результата к паролю. Это действительно полезно только для проверки введенного пароля и не восстановления его снова. На самом деле я никогда не использовал это сам, поэтому я не смог бы вам помочь.

https://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes(v=vs.110).aspx

см. также этот пост, который дает лучшее объяснение, чем я. Как надежно сохранить логин / пароль (локальный)?

Если я неправильно понял вопрос, скажите, я постараюсь обновить ответ.

Обратите внимание, что современные приложения / метро не имеют этой проблемы, хотя они все еще доступны другими способами.

Суровая правда в том, что хранить пароль в настольном приложении на 100% безопасно просто невозможно. Однако вы можете приблизиться к 100%.

Что касается вашего первоначального подхода, PasswordVault использует сервис Credential Locker, который встроен в окна для безопасного хранения данных. Блокировка учетных данных привязана к профилю пользователя. Поэтому хранение ваших данных с помощью PasswordVault по существу эквивалентно подходу с использованием мастер-пароля для защиты данных, о котором я подробно расскажу ниже. Разница лишь в том, что главный пароль в этом случае - учетные данные пользователя. Это позволяет приложениям, запущенным во время сеанса пользователя, получать доступ к данным.

Примечание: для ясности, я строго говорю о том, чтобы хранить его так, чтобы вы могли получить доступ к простому тексту. То есть хранить его в зашифрованной базе данных любого типа или зашифровывать самостоятельно и хранить зашифрованный текст где-то. Такая функциональность необходима в таких программах, как менеджеры паролей, но не в программах, которые просто требуют какой-либо аутентификации. Если в этом нет необходимости, я настоятельно рекомендую хешировать пароль, в идеале в соответствии с инструкциями, изложенными в этом ответе zaph. (Еще немного информации в этом замечательном посте MattBelanger).


Если это необходимо, все становится немного сложнее: если вы хотите запретить другим программам (или пользователям, я полагаю) возможность просматривать незашифрованный пароль, тогда единственный реальный вариант - зашифровать его. Хранение зашифрованного текста в PasswordVault не является обязательным, поскольку, если вы используете хорошее шифрование, единственным вашим слабым местом является то, что кто-то обнаружит ваш ключ. Поэтому сам зашифрованный текст может храниться где угодно. Это подводит нас к самому ключу.

В зависимости от того, сколько паролей вы на самом деле пытаетесь сохранить для каждого экземпляра программы, вам может не понадобиться беспокоиться о генерации и надежном хранении ключа. Если вы хотите сохранить несколько паролей, то вы можете просто попросить пользователя ввести один мастер-пароль, выполнить некоторые операции с солями и хэшированием и использовать результат в качестве ключа шифрования для всех других паролей. Когда пришло время расшифровки, попросите пользователя ввести его снова. Если вы храните несколько паролей, тогда я настоятельно призываю вас придерживаться этого подхода. Это наиболее безопасный из возможных подходов. Однако для остальной части моего поста я буду полагать, что это нереальный вариант.

Прежде всего, я призываю вас не иметь один и тот же ключ для каждой установки. Создайте новый для каждого экземпляра вашей программы на основе надежно сгенерированных случайных данных. Не поддавайтесь искушению "избежать необходимости хранить ключ", генерируя его "на лету" каждый раз, когда это необходимо, основываясь на информации о системе. Это так же безопасно, как жесткое кодирование string superSecretKey = "12345"; в вашу программу. Злоумышленникам не понадобится много времени, чтобы понять процесс.


Теперь хранить его - самая сложная часть. Общее правило infosec следующее:

Ничто не безопасно, если у вас есть физический доступ

Так что в идеале никто бы не стал. Хранение ключей шифрования на правильно защищенном удаленном сервере сводит к минимуму вероятность его восстановления злоумышленниками. Целые книги были написаны о безопасности на стороне сервера, поэтому я не буду обсуждать это здесь.

Другим хорошим вариантом является использование HSM ( Hardware Security Module). Эти изящные маленькие устройства созданы для работы. Доступ к ключам, хранящимся в HSM, практически невозможен. Тем не менее, этот вариант доступен только в том случае, если вы точно знаете, что на каждом компьютере пользователя есть один из них, например, в корпоративной среде.

.Net предлагает решение с помощью системы конфигурации. Вы можете хранить свой ключ в зашифрованном разделе вашего app.config, Это часто используется для защиты соединительных строк. Есть много ресурсов о том, как это сделать. Я рекомендую этот фантастический пост в блоге, который расскажет вам большую часть того, что вам нужно знать.


Причина, по которой я сказал ранее не просто генерировать ключ на лету, состоит в том, что, подобно хранению его в виде переменной в вашем коде, вы полагаетесь исключительно на запутывание, чтобы сохранить его в безопасности. Суть этого подхода в том, что обычно нет. Однако иногда у вас нет другого выбора. Введите белую коробку криптографии.

Криптография белого ящика - это по сути запутанность, доведенная до крайности. Он должен быть эффективным даже в сценарии "белого ящика", когда злоумышленник имеет доступ и может изменять байт-код. Это воплощение безопасности через безвестность. В отличие от простого постоянного сокрытия (Infosec говорят за string superSecretKey подход) или генерирование ключа, когда это необходимо, криптография белого ящика в основном основывается на создании самого шифра на лету.

На нем написаны целые бумаги. Трудно выполнить правильную реализацию, и ваш пробег может варьироваться. Вы должны учитывать это, только если вы действительно действительно хотите сделать это максимально безопасно.


Запутывание однако все еще является запутыванием. Все, что он действительно может сделать, это замедлить нападавших. Окончательное решение, которое я могу предложить, может показаться обратным, но оно работает: не скрывайте ключ шифрования в цифровой форме. Скрыть это физически. Попросите пользователя вставить USB-накопитель, когда наступит время шифрования, (безопасно) сгенерировать случайный ключ, а затем записать его на USB-накопитель. Затем, когда наступит время расшифровки, пользователь должен только вставить диск обратно, и ваша программа считывает ключ с него.

Это немного похоже на подход с мастер-паролем в том смысле, что он оставляет за пользователем право хранить ключ в безопасности. Тем не менее, он имеет некоторые заметные преимущества. Например, этот подход позволяет использовать массивный ключ шифрования. Ключу, который может поместиться в файле размером всего 1 мегабайт, может потребоваться буквально миллиарды лет для взлома методом грубой силы. Плюс, если ключ когда-либо будет обнаружен, пользователь должен винить только себя.


В общем, посмотрите, сможете ли вы избежать необходимости хранить ключ шифрования. Если вы не можете, избегайте его локального хранения любой ценой. В противном случае ваш единственный вариант - сделать так, чтобы хакерам было так трудно это понять. Независимо от того, как вы решите это сделать, убедитесь, что все ключи разные, поэтому даже если злоумышленники найдут один, ключи других пользователей будут в безопасности.

Единственная альтернатива - зашифровать пароль своим личным ключом, хранящимся где-то в вашем коде (Кто-то может легко разобрать ваш код и получить ключ), а затем сохранить зашифрованный пароль в PasswordVault, однако единственная ваша защита - любое приложение не будет иметь доступа к паролю.

Это двойная защита: в случае взлома компьютеров злоумышленник может получить доступ к PasswordVault, но не к вашему паролю, поскольку ему потребуется еще один закрытый ключ для расшифровки пароля, который будет скрыт где-то в вашем коде.

Чтобы сделать его более безопасным, если вы оставите свой закрытый ключ на своем сервере и предоставите API для шифрования и дешифрования пароля перед сохранением в Vault, это сделает его наиболее безопасным. Я думаю, что именно по этой причине люди перешли на OAuth (хранение OAuth-токена в PasswordVault) и т. Д., А не на сохранение пароля в хранилище.

В идеале, я бы порекомендовал не хранить пароль, а вместо этого получить токен с сервера, сохранить его и использовать этот токен для аутентификации. И сохраните этот токен в PasswordVault.

Всегда возможно повысить безопасность, используя различные стратегии шифрования и хранения. Сделать что-то сложнее - это только сделать поиск данных длиннее, а не невозможным. Следовательно, вам необходимо рассмотреть наиболее подходящий уровень защиты, учитывая стоимость выполнения x время (человек и машина) и стоимость разработки x время.

Если я строго рассмотрю ваш запрос, я бы просто добавил слой (класс, интерфейс) для шифрования ваших паролей. Лучше всего с асимметричным шифрованием (а не RSA). Предположим, что другие программы не имеют доступа к данным вашей программы (программа, файлы или процесс), этого достаточно. Вы можете использовать SSH.NET ( https://github.com/sshnet/SSH.NET) для быстрого достижения этой цели.

Если вы хотите повысить безопасность и обеспечить определенный уровень защиты от бинарного обратного инжиниринга (включая извлечение секретного ключа), я рекомендую небольшую (ограниченную процессом) виртуальную машину с шифрованием (например, Docker, https: //blogs.msdn). Решение на основе https://blogs.msdn.microsoft.com/mvpawardprogram/2015/12/15/getting-started-with-net-and-docker/), например Denuvo ( https://www.denuvo.com/). Шифрование является уникальным для каждого клиента и компьютера. Вы должны будете инкапсулировать вашу программу на C# в программу a c/ C++ (которая действует как контейнер), которая будет выполнять все шифрование-дешифрование в памяти.

Вы можете реализовать свою собственную стратегию, в зависимости от вида инвестиций и гарантии, которые вам требуются.

Если ваша программа является бэкэнд-программой, вы можете выбрать лучшую стратегию (единственную, которую я действительно рекомендую) из всех: хранить закрытый ключ на стороне клиента, открытый ключ на стороне сервера и иметь локальное дешифрование, все передаваемые пароли будут быть зашифрованным. Я хотел бы отметить, что пароль и ключи на самом деле являются разными стратегиями для достижения одной и той же цели: проверка, разговаривает ли программа с нужным человеком, не зная его личности; Я имею в виду следующее: вместо хранения паролей лучше хранить непосредственно открытые ключи.

Пересмотр этой довольно полезной проблемы и добавление дополнительной информации, которая может быть полезна.

Моя задача заключалась в том, чтобы расширить приложение Win32, которое использует пароли для аутентификации в онлайн-сервисе, с функцией «сохранения пароля». Идея заключалась в том, чтобы защитить пароль с помощью Windows Hello ( ). У меня сложилось впечатление, что в Windows наверняка есть что-то сравнимое с связкой ключей macOS.

Если вы используете API диспетчера учетных данных Windows ( , ), другое приложение может просто перечислить учетные данные, и если оно знает, что искать (целевое имя), оно сможет прочитать учетные данные.

Я также исследовал использование DPAPI, когда вы сами отвечаете за хранение зашифрованного большого двоичного объекта, обычно в файле. Опять же, похоже, нет никакого способа (кроме обфускации) помешать другому приложению найти и прочитать этот файл. Подача дополнительной энтропии в и снова ставит вопрос о том, где хранить энтропию (обычно я предполагаю, что это будет жестко закодировано и, возможно, запутано в приложении: это безопасность через неясность).

Как оказалось, ни DPAPI ( , ) ни API диспетчера учетных данных Windows ( , ) может помешать другому приложению, работающему под тем же пользователем, прочитать секрет.

На самом деле я искал что-то вроде связки ключей macOS, которая позволяет приложениям хранить секреты, определять списки управления доступом для этих секретов, применять биометрическую аутентификацию при доступе к секрету и, что особенно важно, предотвращает чтение секретов другими приложениями.

Как оказалось, в Windows есть который утверждает, что изолирует приложения друг от друга, но доступен только для приложений UWP:

Представляет Credential Locker учетных данных. Содержимое шкафчика зависит от приложения или службы. Приложения и службы не имеют доступа к учетным данным, связанным с другими приложениями или службами.

Есть ли способ для настольного приложения Win32 получить доступ к этой функции? Я понимаю, что если пользователя можно заставить установить и запустить случайное приложение, это приложение, вероятно, могло бы имитировать исходное приложение и просто предлагать пользователю ввести секрет, но все же немного разочаровывает то, что нет разделения на уровне приложений по умолчанию.

Другие вопросы по тегам