Как создать HashCode в .net(С#) для строки, безопасной для хранения в базе данных?
В цитате из Руководства и правила для GetHashCode Эрика Липперта:
Правило: Потребители GetHashCode не могут полагаться на то, что он стабилен с течением времени или через приложения.
Предположим, что у вас есть объект Customer у которого есть куча таких полей, как Name, Адрес и т.д. Если вы сделаете два такие объекты с точно такими же данных в двух разных процессах, они не нужно возвращать один и тот же хэш код. Если вы сделаете такой объект на Вторник в один процесс, выключите его, и снова запустить программу В среду хэш-коды могут быть отличается.
Это укусило людей в прошлом. Документация для Заметки System.String.GetHashCode в частности, что два идентичных строки могут иметь разные хэш-коды в разных версиях CLR и на самом деле они это делают. Не хранить хеши в базах данных и ожидать, что они будут неизменными навсегда, потому что они не будут.
Итак, каков правильный способ создания HashCode строки, которую я могу хранить в базе данных?
(Пожалуйста, скажите мне, что я не первый человек, который оставил эту ошибку в программном обеспечении, которое я написал!)
Ответы
Ответ 1
Это зависит от того, какие свойства вы хотите, чтобы иметь хэш. Например, вы можете просто написать что-то вроде этого:
public int HashString(string text)
{
// TODO: Determine nullity policy.
unchecked
{
int hash = 23;
foreach (char c in text)
{
hash = hash * 31 + c;
}
return hash;
}
}
Пока вы документируете, что так вычисляется хеш, это действительно. Это никоим образом не криптографически безопасно или что-то в этом роде, но вы можете сохранить его без проблем. Две строки, которые абсолютно равны в порядковом смысле (т.е. Без какого-либо культурного равенства и т.д., Точно одинаковые по характеру, будут иметь одинаковый хэш с этим кодом.
Проблемы возникают, когда вы полагаетесь на недокументированное хеширование - то есть что-то, что подчиняется GetHashCode()
, но никоим образом не гарантируется, что оно останется неизменным с версии на версию... например string.GetHashCode()
.
Написание и документирование вашего собственного хэша, как это, немного напоминает высказывание: "Эта конфиденциальная информация хешируется MD5 (или что-то еще)". Пока это четко определенный хеш, это прекрасно.
EDIT: Другие ответы предложили использовать криптографические хеши, такие как SHA-1 или MD5. Я бы сказал, что до тех пор, пока мы не узнаем, что требуется криптографическая безопасность, а не просто стабильность, нет смысла переходить к тому, чтобы преобразовать строку в массив байтов и хешировать. Конечно, если хеш предназначен для использования в любых связанных с безопасностью, стандартная хэш-версия - именно то, к чему вы должны стремиться. Но об этом не упоминалось нигде в вопросе.
Ответ 2
Например, вы можете создать хэш MD5.
Ответ 3
Вот повторная реализация текущий способ .NET вычисляет строковый хеш-код для 64-битных систем. Это не использует указатели, как реальный GetHashCode()
, поэтому он будет немного медленнее, но делает его более устойчивым к внутренним изменениям в string
, это даст более равномерно распределенный хэш-код, чем Jon Skeet, что может привести к лучшему времени поиска в словарях.
public static class StringExtensionMethods
{
public static int GetStableHashCode(this string str)
{
unchecked
{
int hash1 = 5381;
int hash2 = hash1;
for(int i = 0; i < str.Length && str[i] != '\0'; i += 2)
{
hash1 = ((hash1 << 5) + hash1) ^ str[i];
if (i == str.Length - 1 || str[i+1] == '\0')
break;
hash2 = ((hash2 << 5) + hash2) ^ str[i+1];
}
return hash1 + (hash2*1566083941);
}
}
}
Ответ 4
Ответ заключается в том, чтобы просто написать собственную функцию хэширования. Вы можете найти источник для некоторых из следующих ссылок в комментариях к статье, которую вы опубликовали. Или вы можете использовать встроенную хэш-функцию, которая первоначально предназначалась для криптографии (MD5, SHA1 и т.д.) И просто не использовать все биты.