Проверка пароля - Как безопасно проверить правильность введенного пароля

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

Вот как я думаю сделать это:

У меня есть ключевое слово, скажем гипотетически:

Банан

Когда пользователь вводит свой пароль, я использую RNCryptor для шифрования Banana с помощью введенного им ключа и сохраняю эту зашифрованную строку на сервере.

Позже, когда кто-то пытается ввести пароль, я беру хешированное значение с сервера и пытаюсь расшифровать его, используя пароль, который они ввели в качестве ключа. Если расшифрованное значение равно Banana, я знаю, что они ввели правильный пароль.

Я новичок в безопасности, поэтому не уверен, что это подходящее решение. Вся помощь приветствуется.

Обновлять

После внесения некоторых изменений, предложенных @Greg и метко названных @Anti-weakpasswords, вот что у меня есть:

- (NSDictionary *) getPasswordDictionaryForPassword:(NSString *)password {

    NSData * salt = [self generateSalt256];
    NSData * key = [RNCryptor keyForPassword:password salt:salt settings:mySettings];

    NSMutableDictionary * passwordDictionary = [NSMutableDictionary new];

    NSString * saltString = stringFromData(salt);
    NSString * keyString = stringFromData(key);

    passwordDictionary[@"key"] = keyString;
    passwordDictionary[@"salt"] = saltString;
    passwordDictionary[@"version"] = @"1.0.0";
    passwordDictionary[@"iterationCount"] = @"10000";

    return passwordDictionary;
}

static const RNCryptorKeyDerivationSettings mySettings = {
    .keySize = kCCKeySizeAES256,
    .saltSize = 32,
    .PBKDFAlgorithm = kCCPBKDF2,
    .PRF = kCCPRFHmacAlgSHA1,
    .rounds = 10000
};

- (NSData *)generateSalt256 {
    unsigned char salt[32];
    for (int i=0; i<32; i++) {
        salt[i] = (unsigned char)arc4random();
    }
    NSData * dataSalt = [NSData dataWithBytes:salt length:sizeof(salt)];
    return dataSalt;
}

person Logan    schedule 21.03.2014    source источник
comment
ваше решение выглядит хорошо, но ИМХО лучший способ - хранить хэш на сервере и отправлять каждый пароль от клиента для проверки   -  person sage444    schedule 21.03.2014


Ответы (2)


  • Не используйте один проход любой функции хеширования для хранения паролей.
  • Обязательно используйте случайную соль в диапазоне 8-16 байт.
  • Не используйте обратимое шифрование для хранения паролей.
  • Не используйте введенный пароль точно так же, как ваш ключ шифрования.

Вместо этого, когда пользователь выбирает ключевое слово/парольную фразу

  • Генерировать криптографически случайную 8-16-байтовую соль
  • Use PBKDF2, BCrypt, or SCrypt with said salt and as large an iteration count/work factor as your processors can handle to create a password hash
    • If you use PBKDF2 in specific, do not request a larger output than the native hash size (SHA-1 = 20 bytes, SHA-256 is 32 bytes, SHA-384 is 48 bytes, and SHA-512 is 64 bytes), or you increase the comparative advantage an attacker has over you, the defender.

Затем в вашей базе данных вы сохраняете данные этого пользователя:

  • Соль в чистоте
  • Iteration count/work factor
    • So you can easily change/upgrade it later
  • Полученный хэш пароля
  • Version of authentication protocol - this would be 2, probably, or 1.
    • So you can easily change/upgrade it later if you move from this method to NewWellKnownMethod later

Когда пользователь хочет пройти аутентификацию в вашей системе, вы:

  • Получить их версию, соль, количество итераций/коэффициент работы и результирующий хэш из базы данных.
  • Хэшируйте любое ключевое слово/пароль, которые они только что ввели, с солью и количеством итераций/коэффициентом работы из базы данных.
  • Compare the result you just got with what was in the database; if they're the same, let them in.
    • Advanced: use a constant time compare, so it doesn't just quit trying if the first byte is different, to reduce the vulnerability to timing attacks.

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

person Anti-weakpasswords    schedule 22.03.2014
comment
Большое спасибо за ответ, не могли бы вы сделать краткую проверку того, как я переосуществил это? Должна ли соль быть от 8 до 16, а не 32 в моем примере? - person Logan; 23.03.2014
comment
Более длинная соль не проблема; это просто не польза. Попробуйте провести бенчмаркинг и выберите, сколько итераций займет время, которое вы готовы потратить (хотя 10000 — разумный жесткий минимум для SHA-1). Предполагая, что вы работаете в 64-битной системе, переход на kCCPRFHmacAlgSHA384 или kCCPRFHmacAlgSHA512 уменьшит относительное преимущество графического процессора 2014 года выпуска над вашим ЦП из-за 64-битных операций, которые плохо выполняются на графическом процессоре текущего поколения. Вы также можете проверить arc4random(); Во FreeBSD, по крайней мере, однажды были проблемы с энтропией сразу после загрузки (SA-08.11). - person Anti-weakpasswords; 23.03.2014
comment
Обратите внимание, что вы можете получить гораздо более тщательный общий обзор на сайте Code Review Stackexchange. - person Anti-weakpasswords; 23.03.2014
comment
Я подниму итерации настолько высоко, насколько смогу. Я не знаю, как заменить arc4random(), но посмотрю, что найду. Спасибо за объяснение некоторых вещей! - person Logan; 23.03.2014
comment
Отлично на итерации увеличение! Я бы не сказал заменить arc4random(), но я бы посоветовал проверить и посмотреть, есть ли какие-либо серьезные недостатки в вашей конкретной системе. - person Anti-weakpasswords; 23.03.2014

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

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

Большая часть аутентификации выполняется таким образом.

//Расширенный

Вы должны использовать некоторый хеш-алгоритм, созданный для такого рода работы.

Проверьте SHA или MD5

person Greg    schedule 21.03.2014
comment
Я заметил, что с RNCryptor я иногда получаю разные хэши для одного и того же значения. Не вызовет ли это осложнений, если не будет генерироваться тот же самый хэш? - person Logan; 21.03.2014
comment
@Greg SHA или MD5 на самом деле не шифрование в наше время - person sage444; 21.03.2014
comment
@ sage444 извините, что вы имеете в виду? SHA1 или MD5 — это односторонняя функция хеширования, которая широко используется для хранения паролей. - person Greg; 21.03.2014
comment
Я имею в виду, что это старые алгоритмы, и есть много примеров, когда его взломали. Но вы правы в том, что хеширование — это, безусловно, правильный способ сохранить пароли. - person sage444; 21.03.2014
comment
Грег и @Sage444. Несмотря на то, что мой текущий метод не предполагает сохранения фактического пароля пользователя где-либо, это все же будет предпочтительным решением? - person Logan; 21.03.2014
comment
да. Ваш пример - ключевое слово банана - это пароль, поэтому вы не должны сохранять его как чистую строку в базе данных, вместо этого это должен быть хэш. Вам не нужно сохранять пароль пользователя, просто хешируйте его и сравните с хешем из базы данных. - person Greg; 21.03.2014
comment
@Логан, да, верно - person sage444; 21.03.2014
comment
@ Грег Я думаю, что MD5 следует удалить сверху. На самом деле не очень хорошая идея рекомендовать его. - person CtrlDot; 22.03.2014