Хранение хэшированных паролей - base64 или шестнадцатеричной строки или что-то еще?

Я использую пароль хеширования, используя класс .NET System.Security.Cryptography. Он имеет несколько алгоритмов для хеширования, например. MD5, SHA1, SHA256, SHA384, SHA512

Полученное хешированное значение является байтовым массивом. Должен ли я преобразовать его в шестнадцатеричную строку для хранения, или Convert.ToBase64String(), или что-то еще? (Я предпочитаю Base64, поскольку он короче Hex).

Кстати, с таким количеством хэширования, которое я выбрал, я случайно выбрал SHA384, но есть ли тот, который "лучше" или подходит для этой задачи?

Комментарии.

Обновление после прочтения первых восьми комментариев:
Следуя ответам и дальнейшему чтению, я сделал, кажется, MD5, SHA1 более или менее эквивалентны (при этом SHA1 немного более безопасен). SHA256, 384, 512 обеспечивают еще лучшую защиту в возрастающем порядке.

Так как мне не понадобится fort-knox (это для внутренней корпоративной системы без URL-адресов, браузеров, интернетов, интрасетей или экстрасети в поле зрения), я обойду бизнес "соления" - я понял, могут украсть таблицу паролей, они могут также украсть фактические данные в других таблицах.

Но я буду придерживаться концепции "соли" для дальнейшего использования; не уверен, что соль должна быть добавлена ​​(в конце) или добавлена ​​(спереди) пароля перед хэшированием, и будет ли это иметь значение? Кроме того, я думал о том, чтобы использовать первые несколько символов пароля как соли, чтобы избежать лишнего поля для его хранения, но я думаю, что это не достаточно долго - и соль должна быть достаточно длинной.

Консенсус говорит, что преобразование base64 является разумным выбором для хранения и сравнения. Мне остается выяснить, какая длина столбца максимальной базы данных мне понадобится для хранения хэшей, учитывая максимальную длину пароля в 15 символов. Возможно, Varchar (64)?

Спасибо всем за ваш вклад.

Ответы

Ответ 1

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

Соление делает словарные атаки более дорогими. Решив, какой размер соли использовать, вы можете точно настроить свои шансы. Здесь цитата из Брюса Шнайера "Прикладная криптография":

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

Здесь образец в С#. Это не так сложно. И вы можете выбрать, какой размер соли и хеш-функцию использовать. Отказ от ответственности: используйте что-то вроде bcrypt, если вы действительно заботитесь о целостности пароля.


using System;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;

public class PassHash {
    private static readonly RandomNumberGenerator rng = RandomNumberGenerator.Create();
    public static readonly int DefaultSaltSize = 8; // 64-bit salt
    public readonly byte[] Salt;
    public readonly byte[] Passhash;

    internal PassHash(byte[] salt, byte[] passhash) {
        Salt = salt;
        Passhash = passhash;
    }

    public override String ToString() {
        return String.Format("{{'salt': '{0}', 'passhash': '{1}'}}",
                             Convert.ToBase64String(Salt),
                             Convert.ToBase64String(Passhash));
    }

    public static PassHash Encode<HA>(String password) where HA : HashAlgorithm {
        return Encode<HA>(password, DefaultSaltSize);
    }

    public static PassHash Encode<HA>(String password, int saltSize) where HA : HashAlgorithm {
        return Encode<HA>(password, GenerateSalt(saltSize));
    }

    private static PassHash Encode<HA>(string password, byte[] salt) where HA : HashAlgorithm {
        BindingFlags publicStatic = BindingFlags.Public | BindingFlags.Static;
        MethodInfo hasher_factory = typeof (HA).GetMethod("Create", publicStatic, Type.DefaultBinder, Type.EmptyTypes, null);
        using (HashAlgorithm hasher = (HashAlgorithm) hasher_factory.Invoke(null, null))
        {
            using (MemoryStream hashInput = new MemoryStream())
            {
                hashInput.Write(salt, 0, salt.Length);
                byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
                hashInput.Write(passwordBytes, 0, passwordBytes.Length);
                hashInput.Seek(0, SeekOrigin.Begin);
                byte[] passhash = hasher.ComputeHash(hashInput);
                return new PassHash(salt, passhash);
            }
        }
    }

    private static byte[] GenerateSalt(int saltSize) {
        // This generates salt.
        // Rephrasing Schneier:
        // "salt" is a random string of bytes that is
        // combined with password bytes before being
        // operated by the one-way function.
        byte[] salt = new byte[saltSize];
        rng.GetBytes(salt);
        return salt;
    }

    public static bool Verify<HA>(string password, byte[] salt, byte[] passhash) where HA : HashAlgorithm {
        // OMG: I don't know how to compare byte arrays in C#.
        return Encode<HA>(password, salt).ToString() == new PassHash(salt, passhash).ToString();
    }
}

Использование:

Новый пользователь отправляет свои учетные данные.

PassHash ph = PassHash.Encode<SHA384>(new_user_password);

Хранить ph.Salt и ph.Passhash где-то... Позже, когда пользователь снова войдет в систему, вы запись пользователя, содержащая соль и пропускную карту, а затем вы делаете это:

PassHash.Verify<SHA384>(user_login_password, user_rec.salt, user_rec.passhash)

}

Ответ 2

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

Это все предполагается, что вы хотите строку, конечно. Строки хороши и удобны в хранении, передаче и т.д. По сравнению с непрозрачными байтовыми массивами.

Ответ 3

Кстати, с таким количеством хэширования, которое я выбрал, я случайно выбрал SHA384, но есть ли тот, который "лучше" или подходит для этой задачи?

В общем, я бы избегал MD5 или SHA-0 или SHA-1. Но SHA-384 должно быть достаточным для ваших нужд на данный момент. См. этот вопрос для обсуждения алгоритмов.

Ответ 4

Интересная заметка, которую я видел раньше, если у вас есть массив байтов, и вы кодируете его base64, некоторые из полученных символов могут вызвать проблемы с URL-адресами - однако, если вы заново кодируете код 64 во второй раз, эти нарушающие символы, как правило, удаляются, а строка base64 может использоваться в URL-адресах. Вы можете сравнить, используя строку с двойным кодированием.

В алгоритмах хэширования sha384, вероятно, является достаточным алгоритмом, но убедитесь, что вы соедините свой хэш!

Ответ 5

Чтобы ответить на вторую часть вашего вопроса, SHA384, вероятно, перебор. SHA1 будет хорошо служить вашим целям, но для него существует теоретический подход (связанный с возможностью создания измененного документа, который хэширует до того же значения, о чем вам не о чем беспокоиться).

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

Для записи мне нравится Base64, поскольку .NET имеет встроенные процедуры для преобразования в/из него.

Ответ 6

Вы также можете преобразовать его в простые байты. Таким образом, 128-битное MD5-хэш-значение приведет к длине строки длиной 128 бит /8 бит/байт = 16 байтов.

Ответ 7

Для паролей я считаю, что любой из них отлично работает. SHA 512 создаст ключ выше, уменьшив вероятность "столкновения", зная, что разные пароли могут иметь один и тот же хэш, но возможность очень низкая.

Ответ 8

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

Вам следует добавить соль в хешированные данные. Это случайное значение, которое помещается перед паспортом, который вы используете. Затем сохраните значение соли с хешированным значением, чтобы вы могли восстановить значение хэша.

Роль этой соли заключается в том, чтобы не допустить, чтобы построение dictionnary сопоставило общие пароли с хешированными значениями. Если кто-то может получить копию вашего файла хешированного пароля, он может использовать этот словарь для поиска текстового пароля. С 16-битной солью вы имеете 65536 разных значений хэша для одного и того же текстового пароля. Атака на тиражирование становится менее эффективной. Лучше выбрать 64-битную соль, потому что она не добавит больше усилий для хеширования и проверки паролей.

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

Что касается алгоритмов использования SHA1, это очень хорошо, и SHA256 будет поясом и ремнями. MD5 намного дешевле вычислить и удовлетворит большинство случаев использования. Рассмотрите это, если ресурс обработки и хранения может быть ограничивающим фактором.

Ответ 9

Я бы использовал Base64.... oh и не забыл солить ваш хэш, если его пароль вы хешируете:) И что-то меньшее, чем SHA384, является угрозой безопасности, если мы говорим о пароле, потому что так легко получить качество радужные столы для SHA1 и других распространенных хеширующих альго.

Ответ 10

Как использовать нотацию Base36 или Base62, которая содержит только алфавиты и цифры (Safely может передавать в URLS). Я просто попробовал следующее в java

BigInteger hexNumber = new BigInteger(hexString, 16);
// Convert it to Base 36 yields smaller string than hexString
hexNumber.toString(36);

Преобразуйте номер Base36 обратно в шестнадцатеричный

BigInteger b36String= new BigInteger(b36String, 36);
// Convert it to Base 16 yields original hex string
hexNumber.toString(16);

BTW Java BigInteger Максимальное значение оснований - 36.

Для Base62 У нас может быть таблица поиска с 62 символами (26 нижних, 26 верхних и 10 цифр) математически делить шестнадцатеричный номер на 64 и продолжать добавлять символ, полученный из таблицы поиска, используя остаток.

Предложения приветствуются.