Ответ
Пароли пользователей никогда не должны храниться в открытом виде (plaintext). Единственный безопасный способ — хранить их хеши с использованием криптографически стойких, медленных алгоритмов.
Ключевые принципы:
Используйте медленный алгоритм хеширования. Быстрые алгоритмы (MD5, SHA-256) не подходят, так как они уязвимы для атак перебором (brute-force). Рекомендуемые алгоритмы:
- Bcrypt: Надежный и широко используемый стандарт.
- Scrypt: Требует больше памяти, что делает его еще более устойчивым к аппаратным атакам.
- Argon2: Победитель конкурса Password Hashing Competition, считается самым современным и надежным стандартом.
Используйте соль (Salt). Соль — это случайная строка, которая добавляется к паролю перед хешированием. Это предотвращает атаки с использованием "радужных таблиц" (rainbow tables). Современные библиотеки, такие как
bcrypt
в Go, генерируют и встраивают соль в хеш автоматически.Настраивайте "стоимость" (Cost Factor). У медленных алгоритмов есть параметр "стоимости" или "количества раундов", который определяет, насколько ресурсоемким будет процесс хеширования. Его нужно подбирать так, чтобы хеширование занимало приемлемое время (например, 100-500 мс) на вашем оборудовании.
Пример реализации на Go с bcrypt
:
package main
import (
"fmt"
"golang.org/x/crypto/bcrypt"
)
// HashPassword хеширует пароль с использованием bcrypt
func HashPassword(password string) (string, error) {
// bcrypt.DefaultCost равен 10. Для продакшена рекомендуется 12-14.
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
return string(bytes), err
}
// CheckPasswordHash сравнивает пароль с хешем
func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
func main() {
password := "mySecurePa$w0rd"
hash, _ := HashPassword(password)
fmt.Println("Password:", password)
fmt.Println("Hash: ", hash)
match := CheckPasswordHash(password, hash)
fmt.Println("Match: ", match) // Выведет: true
}