Если вы не можете избежать хранения паролей на сервере, то лучшим вариантом будет шифрование с помощью «главного» пароля пользователя (например, «hunter2»). Ни один другой подход не обеспечивает защиту в случае взлома сервера. Теперь ... какая защита вы получаете полностью зависит от сложности мастер-пароля пользователя. В конце я предложу свой анализ безопасности этой схемы, но перед этим позвольте мне рассмотреть подводные камни, которых следует избегать.
Сначала & mdash; и я предполагаю, что вы это уже знаете, но & mdash; вы не должны хранить главный пароль пользователя где-либо . Хорошо, с допущениями в сторону ...
Не используйте действительный пароль пользователя в качестве ключа к функции шифрования.
Подумайте, что было бы возможно, если бы вы это сделали: что если злоумышленнику удастся загрузить всю вашу таблицу users с зашифрованными паролями example.com ? Все мы знаем, что выбранные пользователем пароли легко угадать. Что помешает злоумышленнику неоднократно расшифровывать зашифрованный пароль example.com , пытаясь использовать 40 миллионов часто используемых паролей в качестве ключа, отбрасывая любой результат, не похожий на пароль (который есть, расшифрованный результат не появляется в списке слов)? AES предназначен для быстрого . Хотя это не сравнение между яблоками и яблоками, ощущение скорости AES следует придавать, если учесть, что зашифрованная версия вышеупомянутого списка слов 500 Мб может быть расшифрована примерно за одну секунду на современном оборудовании. Хуже того, злоумышленник получит не только пароль example.com , но и ключ, используемый для шифрования, или, другими словами, главный пароль пользователя!
В двух словах, именно поэтому вам необходимо использовать функцию получения ключа (KDF). KDF идеально защитит вас тремя способами:
- Требуется нетривиальное количество времени для вычисления каждого ключа. Пользователь может подождать одну секунду, пока сервер превратит свой пароль в ключ. Злоумышленник может быть менее склонен ждать 40 000 000 секунд - см. Анализ ниже.
- Соль пароль. Без соли злоумышленник может перебить всю таблицу пользователей за один проход, не говоря уже о пространственно-временном обмене .
- Запретить восстановление мастер-пароля, даже если злоумышленник восстановит ключ шифрования.
Один из таких KDF, который обеспечивает все три: PBKDF2 . Для удобства есть реализация , встроенная в .NET:
public static byte[] DeriveKey(int keyBitSize, string password, byte[] salt) {
const int iterations = 1<<12; // Once set, any change will break decryption.
using (var kdf = new Rfc2898DeriveBytes(password, salt, iterations)) {
return kdf.GetBytes(keyBitSize);
}
}
Анализ
40 миллионов секунд - это менее 500 дней. Поскольку списки слов обычно упорядочиваются в первую очередь по наиболее часто используемым паролям, у злоумышленника есть хороший шанс найти пароль значительно меньше, чем за половину времени, необходимого для проверки всего списка слов. В качестве последнего недостатка можно попробовать ключи параллельно: ботнет из 500 узлов может опробовать весь список слов за день.
В этом и заключается проблема с использованием пароля пользователя для обеспечения безопасности шифрования. Вы можете принять этот риск или отказаться от сохранения пароля пользователя на сервере. Если вы решили хранить зашифрованные пароли на сервере, вы можете снизить риск, увеличив требования к сложности главного пароля пользователя.