Вопрос или проблема
Я разрабатываю приложение для ведения заметок, которое будет хранить заметки и файлы пользователей в зашифрованном виде в базе данных и на Backblaze соответственно. Приложение не будет иметь сквозного шифрования, но данные будут зашифрованы в процессе передачи (с использованием TLS) и в состоянии покоя (AES256 для данных заметок в базе данных и с использованием управляемых клиентом ключей в Backblaze для шифрования файлов). Я хочу зашифровать каждую заметку и файл отдельным ключом шифрования.
Мой план достижения этого на данный момент:
- Пользователь предоставляет адрес электронной почты, пароль и deviceId (модель устройства) для конечной точки “Регистрация”
- Пароль пользователя хешируется с помощью Bcrypt (хранится в базе данных для “пользователя”)
- Генерируется случайная соль (хранится в базе данных для “пользователя”)
- Генерируется KEK с помощью PBKDF2 из открытого пароля пользователя и соли
- Генерируется криптографически случайный DEK, который шифруется с помощью KEK (хранится в базе данных для “пользователя”)
- После успешной регистрации, когда пользователь отправляет “заметку” или “файл”, ключ для этого ресурса выводится из DEK, используя HKDF и UUID ресурса
Я считаю, что описанный выше метод надежен, но он требует пароль для работы. Я создаю мобильное приложение, поэтому хотел бы поддерживать сессию между ним и сервером (используя куки), так что мне нужен способ получить KEK без пароля. Мой текущий план:
- Когда пользователь входит в систему, вывести KEK и зашифровать его с помощью криптографически случайного CookieValue
- Сохранить зашифрованный KEK в таблице “сессий” в базе данных
- Установить куки пользователя на этот CookieValue (установить “HTTPOnly” и “Secure” для куки)
- Теперь, когда пользователь предоставляет свои куки, сервер может расшифровать зашифрованный KEK в “сессиях” и получить то, что ему нужно для шифрования/расшифрования данных пользователя
Часть этого процесса, в надежности которой я меньше всего уверен, это использование куки для валидации пользователя и предоставления серверу возможности шифровать/расшифровывать их данные. Есть ли какие-либо недостатки в этом методе? Есть ли лучший подход к любой из частей (особенно к части сессии)?
Ответ или решение
Чтобы создать безопасный механизм, позволяющий извлечь ключ шифрования из пароля, а затем получить доступ к этому ключу позже без пароля (с использованием cookie), можно предложить следующие шаги, уточняющие и расширяющие ваш первоначальный план.
Предложенный метод:
-
Регистрация пользователя:
- Пользователь предоставляет электронную почту, пароль и идентификатор устройства для регистрации.
- Пароль пользователя хешируется с использованием Bcrypt и хранится в базе данных.
- Генерируется случайная соль, которая также хранится в базе данных для пользователя.
- С помощью PBKDF2 и пароля пользователя, а также сгенерированной соли создается ключ шифрования для ключей (KEK).
- Создается криптографически случайный ключ шифрования данных (DEK), который шифруется с использованием KEK и хранится в базе данных для пользователя.
-
Вход пользователя:
- При попытке войти пользователь предоставляет свою электронную почту и пароль.
- Пароль хешируется и сравнивается с хешем, хранящимся в базе данных.
- Если проверка прошла успешно, генерируется новая случайная соль (Salt), которая используется в PBKDF2 для получения KEK.
-
Использование cookie для сессий:
- После успешного входа вам нужно зашифровать KEK с помощью криптографически случайно сгенерированного ключа (сессионного ключа).
- Сессионный ключ генерируется с помощью, например, AES в режиме GCM, и хранится в базе данных вместе с шифрованным KEK.
- Создается cookie, содержащий значение сессионного ключа, и ему устанавливаются атрибуты
HTTPOnly
иSecure
.
-
Расшифровка KEK:
- При последующих запросах пользователь предоставляет cookie.
- Сервер извлекает сессионный ключ, соответствующий cookie, и расшифровывает с помощью его зашифрованный KEK.
- Теперь сервер может использовать KEK для шифрования и расшифровки данных пользователя.
Обсуждение безопасности:
-
Шифрование KEK: Защита KEK от потенциального компрометации путем шифрования сессионным ключом значительно повышает уровень безопасности. Важно, чтобы сессионный ключ имел достаточную длину и был сгенерирован с использованием надежного криптографического генератора.
-
Управление сессиями: Хорошей практикой будет реализовать механизм истечения срока действия для cookies. Это значит, что после определенного времени пользователь должен будет снова ввести свои учетные данные.
-
Уязвимости: Убедитесь, что она защищает от атак, таких как атаки на повторное воспроизведение. Например, при проверке cookie можно добавить метку времени и уникальный идентификатор сессии, чтобы гарантировать, что одно и то же значение cookie не будет использовано более одного раза.
-
Безопасность пароля: Периодически напоминайте пользователям о необходимости изменения пароля и рекомендовать использование многофакторной аутентификации (MFA) для повышения безопасности их учетных записей.
Заключение:
Ваш оригинальный метод выглядит неплохо, однако имеет смысл усиливать безопасность с помощью предложенных дополнений, таких как шифрование KEK и управление сессионными cookies. Следуя этим рекомендациям, вы сможете создать более безопасный механизм управления ключами и обеспечивать желаемую функциональность приложения.