Comments 6
Спасибо за статью. Извините, что я сейчас буду писать эту критику в комментарии, а не Вам в личку, потому что важно, чтобы люди, которые совсем не разбираются в криптографии, не читали вот это вот и не следовали этим примерам.
Судя по всему Вы тоже просто прочли оригинальную статью и поверили ей на слово.
1.
Разработчики… создают запрос на сервер, который напрямую посылает логин и пароль.
Автор будет чрезвычайно удивлен, узнав, что это… не влияет ни на что! Хэширование, о котором он говорит, должно происходить на сервере при сохранении паролей в базу. И необходимо оно для того, чтобы злые хацкеры не смогли восстановить пароль, если они взломают сервак.
При посылке пароля, как и всех остальных запросов можно и нужно (сорвем же покровы!) использовать TLS. Нет разницы, будете вы посылать пароль в чистом или хэшированом виде. Если канал будет скомпрометирован, то человечек посередине в любом случае его перехватит и получит доступ к вашему аккаунту, хэшируй ты этот пароль на клиенте хоть миллиард раз.
2.
HKDF это функция формирования ключа
Почему использовался HKDF, а не PBKDF2, не scrypt? Просто в образовательных целях: это функции формирования ключа шифрования. То есть прямое назначение этих функций — это из короткого и неслучайного массива байт сделать более длинный и выглядящий как случайный. И "соль" играет здесь огромную роль.
3.
let salt = Array("salty".utf8)
сервер должен знать, что зашифровано в нашей переменной salt. Бэкэнд сможет сравнить ключи используя тот же алгоритм, чтобы идентифицировать пользователя.
Для того, чтобы все вышеперечисленное вообще имело хоть какой-то смысл, соль должна быть а) псевдослучайной, б) разной для каждого пароля. Наша ф-ция выглядит в общем виде так:
f(s, P, a) -> k
P
— это пароль. Секретное, но НЕ случайное значение.
s
— соль. НЕ секретное, но псевдослучайное значение.
a
— алгоритм хэширования, используемый внутри функции формирования ключа.
k
— псевдослучайное значение на выходе.
Обатите внимание, что функция вернет псевдослучайное значение, если ну хотя бы один из ее параметров будет псевдослучайным. Откуда эта случайность вообще появится, если все ее параметры — это "salty"
и (допустим) "mysuperpass"
? Когда соль случайна, то на выходе каждый раз будет новое значение, даже для одних и тех же паролей.
Так вот, соль нужно сгенерировать с помощью криптографически стойкого генератора псевдослучайных значений. Затем прогнать это все дело через KDF, а уж затем сохранить и соль, и выходное значение в базу, если уж так хочется.
Как же сгенерировать по-настоящему псевдослучайное значение? Нужно где-то взять генератор, сгенерить это значение определенной длины, как того требует выбранный Вами алгоритм. Автор использует какой-то CryptoSwift
? Что ж посмотрим, где у него там генератор, заходим на https://github.com/krzyzanowskim/CryptoSwift, на главной странице нет ничего про это. Смотрим исходники, видим файл RandomBytesSequence.swift
, смотрим содержимое:
struct RandomBytesSequence: Sequence {
let size: Int
func makeIterator() -> AnyIterator<UInt8> {
var count = 0
return AnyIterator<UInt8>.init { () -> UInt8? in
//...
#if os(Linux) || os(Android) || os(FreeBSD)
//...
#else
return UInt8(arc4random_uniform(UInt32(UInt8.max) + 1))
#endif
}
}
}
Я даже не знаю, стоит ли этот arc4random_uniform комментировать. Использование? Например, файл Cryptors.swift
:
extension Cryptors {
public static func randomIV(_ blockSize: Int) -> Array<UInt8> {
var randomIV: Array<UInt8> = Array<UInt8>()
randomIV.reserveCapacity(blockSize)
for randomByte in RandomBytesSequence(size: blockSize) {
randomIV.append(randomByte)
}
return randomIV
}
Вот такой вот у нас "рэндомный" IV получился.
Не буду вдаваться в подробности, просто не используйте это либу никогда и ни за что. Почитайте лучше про CSPRNG.
4.
Ну и финальное. Если Вы будете хранить пароль в Keychain, этого будет вполне достаточно, чтобы знать, что он уже зашифрован. Эппловская "свзяка ключей" как раз и использует KDF для того, чтобы зашифровать ВСЕ ваши данные, которые вы в этой связке храните.
Поэтому все действия, описанные в этой статье (кроме того, что не нужно хранить пароли в NSUserDefaults), лишние. И, судя по всему, даже вредные.
Ого, спасибо большое за комментарий, очень интересно! Я действительно переводила статью потому что она мне показалась достаточно понятной начинающим разработчикам (как я), но в который раз убеждаюсь, что копать нужно гораздо глубже и каждый момент разбирать тщательнее.
По поводу п.1 позвольте не согласиться, разница все же есть. Многие используют один и тот же пароль на нескольких сервисах, и если кто-то взломает конкретный TLS канал и узнает пароль в открытом виде — это даёт хакеру потенциальную возможность зайти с этим же паролем в другие сервисы.
Безопасность в iOS приложениях