Блочные шифры, соль, AES, MySQL и лучшие практики хранения учетных данных

У меня есть ситуация, когда я должен сохранить пароль, поскольку я создаю систему для подключения к другой системе. Эта другая система позволяет использовать только одну учетную запись пользователя, и единственный способ подключиться к ней — через пароль. Хэш здесь неуместен. Я должен хранить пароль таким образом, чтобы я мог его восстановить.

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

При сохранении пароля я буду использовать AES_ENCRYPT("password", "encryption key"). Затем я понял, что мне, вероятно, следует использовать немного соли, чтобы, если они смогли получить один пароль, получить другие было бы сложнее. Но ждать! В чем смысл? Если они могут получить один пароль, у них должен быть ключ шифрования, да?

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

/* Returns 8CBAB2A9260975FF965E5A7B02E213628CBAB2A9260975FF965E5A7B02E21362FBB5D173CBAFA44DC406B69D05A2072C */
SELECT HEX(AES_ENCRYPT("passwordpasswordpasswordpassword", "encryption key"));

/* Returns 8CBAB2A9260975FF965E5A7B02E213628CBAB2A9260975FF965E5A7B02E21362C49AF8D5B194770E64FEF88767206391 */
SELECT HEX(AES_ENCRYPT("passwordpasswordpasswordpassworda", "encryption key"));

Мои вопросы

  • Прав ли я, думая, что нет причин использовать соль при использовании симметричного шифрования в таких ситуациях, как моя?

  • Учитывая, что я должен хранить пароль таким образом, чтобы получить исходное значение, есть ли какие-либо другие методы, которые мне следует рассмотреть? (Я знаю, что мне нужно быть осторожным с тем, где и как хранится ключ шифрования, и что мне также нужно защищать свои журналы MySQL.)


person Brad    schedule 31.03.2013    source источник
comment
Вы должны использовать CBC; соль IV.   -  person SLaks    schedule 01.04.2013
comment
Рассмотрите возможность использования асимметричного шифрования, чтобы машина (веб-сервер?), Которая шифрует пароли, не могла их расшифровать (в случае взлома)   -  person SLaks    schedule 01.04.2013
comment
@SLaks, хорошая идея, SLaks! Я не думаю, что MySQL предлагает какие-либо асимметричные алгоритмы, но я полагаю, что в этом случае стоит затрачивать усилия на их реализацию. Я посмотрю на RSA.   -  person Brad    schedule 01.04.2013
comment
@SLaks, приятно видеть, что вы внесли свой вклад в проект, который я только что просматривал: github.com/Obvious/ursa   -  person Brad    schedule 01.04.2013
comment
Пожалуйста! Похоже, мы делаем то же самое. Вы должны сгенерировать случайный симметричный ключ для каждой строки и зашифровать его с помощью ключа RSA. См. также daemonology.net/blog/. .   -  person SLaks    schedule 03.04.2013


Ответы (1)


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

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

E.g.

  1. Создайте случайную последовательность.
  2. Хранить одноразовый номер || HEX(AES_ENCRYPT(пароль_в_хранилище, главный_пароль || одноразовый номер)
  3. Получите, извлекая одноразовый номер, затем расшифруйте данные с помощью master_password || одноразовый

Вот пример с уникальным одноразовым номером iej383u8fjeiw (каждый раз, когда вы шифруете, вам нужно создать новый)

SELECT CONCAT('iej383u8fjeiw', ':', HEX(AES_ENCRYPT("password", CONCAT("master_password", "iej383u8fjeiw")))) 
-> "iej383u8fjeiw:61224653D4DA33D57A42FE5E5E10DEA9"

SELECT AES_DECRYPT(UNHEX(SUBSTRING_INDEX('iej383u8fjeiw:61224653D4DA33D57A42FE5E5E10DEA9', ':', -1)), CONCAT('master_password', SUBSTRING_INDEX('iej383u8fjeiw:61224653D4DA33D57A42FE5E5E10DEA9', ':', 1))) 
-> "password"

Или с переменными:

SELECT CONCAT(nonce, ':', HEX(AES_ENCRYPT(password_to_encrypt, CONCAT(master_password, nonce)))) 
-> encrypted password

SELECT AES_DECRYPT(UNHEX(SUBSTRING_INDEX(encrypted_password, ':', -1)), CONCAT(master_password, SUBSTRING_INDEX(encrypted_password, ':', 1)))
-> password_to_encrypt

Тем не менее, несмотря на то, что он значительно более безопасен, чем версия без одноразового номера, остается множество слабых мест и векторов атак. Например, регистрация запросов или перехват пакетов mysql покажет как пароль, так и мастер-пароль!

person Nuoji    schedule 31.03.2013
comment
Не могли бы вы предложить, какой тип данных я должен использовать для хранения этих данных в таблице и какой должна быть длина данных? - person Nikunj Kabariya; 07.06.2016
comment
@NikunjKabariya В этом примере пароль хранится в БД, поэтому длина будет зависеть от длины пароля. Если вы используете AES-128, то каждый блок имеет длину 16 байт, поэтому минимальная длина будет 16 байт, а верхняя длина зависит от максимальной длины пароля. Это отличается от использования хеша фиксированной длины. Если результат хранится в шестнадцатеричном формате, достаточно использовать столбец varchar. - person Nuoji; 07.06.2016