Какие алгоритмы хеширования вы используете для генерации токенов и паролей?

Ответ

Выбор алгоритма зависит от цели: хеширование паролей и создание криптографических подписей для токенов — это разные задачи.

1. Для хеширования паролей (должно быть медленным):

  • PBKDF2 (Password-Based Key Derivation Function 2): Стандартный выбор в .NET. Использует множество итераций хеш-функции (например, SHA-256) вместе с "солью" (salt), что сильно замедляет перебор.
    using System.Security.Cryptography;
    // Генерация хеша
    public byte[] HashPassword(string password, out byte[] salt)
    {
        salt = RandomNumberGenerator.GetBytes(32); // Уникальная соль для каждого пароля
        using var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 600000, HashAlgorithmName.SHA256);
        return pbkdf2.GetBytes(32); // 256-битный хеш
    }
    // Верификация
    public bool VerifyPassword(string password, byte[] storedHash, byte[] storedSalt)
    {
        using var pbkdf2 = new Rfc2898DeriveBytes(password, storedSalt, 600000, HashAlgorithmName.SHA256);
        byte[] testHash = pbkdf2.GetBytes(32);
        return CryptographicOperations.FixedTimeEquals(storedHash, testHash); // Сравнение, защищенное от атак по времени
    }
  • BCrypt / Argon2: Считаются более устойчивыми к атакам с использованием специализированного железа (ASIC/GPU). В .NET используются через сторонние библиотеки (например, BCrypt.Net-Next).

2. Для подписи токенов (должно быть быстрым и криптостойким):

  • HMAC-SHA256/512 (Hash-based Message Authentication Code): Основа для подписи JWT (JSON Web Tokens). Гарантирует, что токен не был изменен после выдачи.
    using System.Security.Cryptography;
    // Создание подписи для данных
    public byte[] SignData(byte[] data, byte[] secretKey)
    {
        using var hmac = new HMACSHA256(secretKey); // Секретный ключ известен только серверу
        return hmac.ComputeHash(data);
    }
  • Асимметричная подпись (RSA, ECDSA): Используется, когда нужно, чтобы токен мог проверить любая сторона, имеющая открытый ключ, но подписать — только владелец закрытого ключа (например, в микросервисной архитектуре).

Ключевые принципы:

  • Для паролей всегда используйте соль (уникальную для каждого пользователя) и адаптивные функции (большое число итераций).
  • Секретные ключи для HMAC должны генерироваться криптографически стойким генератором и храниться в защищенном месте (Key Vault).
  • Избегайте устаревших алгоритмов: MD5, SHA1 считаются небезопасными для этих целей.

Ответ 18+ 🔞

Давай разберёмся, что куда тыкать, а то народ путает как слепой кот в сортире. Смотри, тут два принципиально разных случая, и если их перепутать — будет тебе хиросима в продакшене, а не система.

1. Хеширование паролей (должно быть МЕДЛЕННЫМ, чтоб взломщики обосрались) Смысл в чём? Надо сделать такую операцию, чтобы проверка одного пароля у тебя занимала, скажем, 100 мс. Это ерунда для одного пользователя. А вот у подлеца, который купил ферму из 1000 видюх и пытается перебрать миллиарды вариантов, это будет пиздец как долго и дорого. Тут скорость — враг.

  • PBKDF2 (Password-Based Key Derivation Function 2): Старый, добрый, проверенный тазик в .NET. Берёт обычный хеш (типа SHA-256) и долбит им по паролю с солью тысячи, а лучше сотни тысяч раз. От этого все устают, особенно брутфорс-боты.
    using System.Security.Cryptography;
    // Генерация хеша
    public byte[] HashPassword(string password, out byte[] salt)
    {
        salt = RandomNumberGenerator.GetBytes(32); // Уникальная соль для каждого юзера! Без неё — вообще никуда.
        using var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 600000, HashAlgorithmName.SHA256); // 600к итераций — не стесняйся
        return pbkdf2.GetBytes(32); // 256-битный хеш
    }
    // Проверка
    public bool VerifyPassword(string password, byte[] storedHash, byte[] storedSalt)
    {
        using var pbkdf2 = new Rfc2898DeriveBytes(password, storedSalt, 600000, HashAlgorithmName.SHA256);
        byte[] testHash = pbkdf2.GetBytes(32);
        return CryptographicOperations.FixedTimeEquals(storedHash, testHash); // Важно! Сравнение, защищённое от атак по времени, чтобы не светить, где ошибка.
    }
  • BCrypt / Argon2: Это уже круче. Они не только итерации делают, но ещё и память жрут как не в себя, что делает атаки на спецжелезе (типа ASIC или GPU) невыгодными. В .NET нет из коробки, но есть через BCrypt.Net-Next. Если делать серьёзно — смотри в их сторону.

2. Подпись токенов (должно быть БЫСТРЫМ и надёжным) Тут задача другая. Ты выпускаешь JWT токен на 15 минут. Его нужно быстро подписать тебе и быстро проверить любому сервису, которому ты дашь ключ. Медлить тут нельзя, запросов много.

  • HMAC-SHA256/512: Классика для JWT. Берёшь данные токена, секретный ключ (который знаешь только ты) и получаешь подпись. Изменишь хоть запятую в токене — подпись не сойдётся. Быстро и сердито.
    using System.Security.Cryptography;
    // Создание подписи
    public byte[] SignData(byte[] data, byte[] secretKey)
    {
        using var hmac = new HMACSHA256(secretKey); // Этот ключ — твоя главная тайна. Храни как зеницу ока.
        return hmac.ComputeHash(data);
    }
  • Асимметричная подпись (RSA, ECDSA): Это когда у тебя есть парочка: приватный ключ (подписывает) и публичный (проверяет). Нужно, если твой API-шник должен проверять токен, но не должен уметь его создавать. Идеально для микросервисов, где один сервис выпускает, а десять других проверяют.

Главное, что нужно вынести, чтобы не быть распиздяем:

  • Пароли: Соль + много итераций = PBKDF2/BCrypt/Argon2. Никакого SHA256 в лоб, ёпта! Это как хранить голый текст, только чуть хуже.
  • Ключи для HMAC: Генерируй их через RandomNumberGenerator, а не new Random(), и прячь в Key Vault, а не в конфиг рядом с кодом. Доверия к файлу appsettings.json — ноль ебать.
  • Старьё: Забудь как страшный сон про MD5, SHA1. Они сломаны, их бьют как детей. Использовать их — это кричать всему миру «я мудак, блядь, нахуй идите ко мне в прод».
  • Не изобретай велосипед: Не пытайся склеить PBKDF2 с HMAC или придумать свою «супер-защиту». Возьми готовый, проверенный боем алгоритм и правильно его настрой. Всё уже придумано до нас, и чаще всего — умными людьми, которые в этом шариют.

Вот так, коротко и без воды. Выбирай инструмент по задаче, а не потому что «в гайде на метаните так было».