Лучший алгоритм для хеширования числовых значений? - PullRequest
10 голосов
/ 01 сентября 2009

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

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

Целевым языком является Delphi, однако ответы на других языках приветствуются, если они могут обеспечить математическую основу, которая может привести к оптимальному решению.

Целью этой процедуры будет определение того, была ли ранее обработанная карта / счет ранее обработана или нет.Входной файл может иметь несколько записей в базе данных из нескольких записей, поэтому производительность является фактором.

Ответы [ 8 ]

12 голосов
/ 01 сентября 2009

С вопросами безопасности все ответы лежат на континууме от наиболее безопасных до наиболее удобных . Я дам вам два ответа, один из которых очень безопасный, а другой очень удобный. Учитывая это и объяснение каждого из них, вы можете выбрать лучшее решение для вашей системы.

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

Удобное решение заключается в использовании FNV (как предложили Zebrabox и Ник). Это даст 32-битное число, которое будет быстро индексироваться для поиска. Недостатком, конечно, является то, что он допускает не более 4 миллиардов различных чисел, и на практике вызовет столкновения гораздо быстрее, чем это. Поскольку у него такой высокий уровень столкновений, атака грубой силой, вероятно, даст достаточно неверных результатов, чтобы сделать ее малопригодной.

Безопасное решение заключается в использовании хэш-функции SHA (чем больше, тем лучше), но с несколькими итерациями. Я бы предложил где-то порядка 10000. Да, я знаю, 10 000 итераций - это много, и это займет некоторое время, но когда дело доходит до силы против грубой силы, скорость атаки противника. Если вы хотите быть в безопасности, то вы хотите, чтобы это было МЕДЛЕННО. SHA разработан так, чтобы не было коллизий при любом размере ввода. Если обнаружено столкновение, то хеш считается более нежизнеспособным. AFAIK семья SHA-2 все еще жизнеспособна.

Теперь, если вам нужно решение, которое безопасное и быстрое для поиска в БД, тогда я бы предложил использовать безопасное решение (SHA-2 x 10K) и затем сохранить полный хеш в одном столбце. и затем возьмите первые 32 бита и сохраните их в другом столбце с индексом во втором столбце. Сначала выполните поиск 32-битного значения. Если это не дает совпадений, то у вас нет совпадений. Если оно дает совпадение, то вы можете сравнить полное значение SHA и посмотреть, совпадает ли оно. Это означает, что вы выполняете полное двоичное сравнение (хеши на самом деле являются двоичными, но представлены только в виде строк для удобного чтения человеком и для передачи в текстовых протоколах) на гораздо меньшем множестве.

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

Кроме того, я бы порекомендовал вам тест поиск полного хеша по сравнению с 32-битным подмножеством. Большинство современных систем баз данных являются довольно быстрыми и содержат ряд оптимизаций и часто оптимизируют для нас, делая вещи просто easy . Когда мы пытаемся стать умными, мы иногда просто замедляем это. Что это за цитата о преждевременной оптимизации? , ,

6 голосов
/ 01 сентября 2009

Это похоже на функции вывода ключа . Взгляните на PBKDF2 .

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

UPDATE

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

Другая проблема, вероятно, состоит в том, что числа образуют большие кластеры назначенных (учетных) номеров с большими областями неназначенных номеров между ними. В этом случае я бы предложил попробовать сильно нелинейную хеш-функцию для распространения этих кластеров. И это возвращает нас к криптографическим хеш-функциям. Может быть, старый добрый MD5. Просто разбейте 128-битный хэш на четыре группы по 32 бита, объедините их, используя XOR, и интерпретируйте результат как 32-битное целое число.

Хотя это и не имеет прямого отношения, вы также можете взглянуть на закон Бенфорда - он дает некоторое представление о том, почему числа обычно распределяются неравномерно.

3 голосов
/ 01 сентября 2009

Если вам нужна безопасность, используйте криптографически безопасный хеш, такой как SHA-256.

2 голосов
/ 05 сентября 2009

Несколько месяцев назад мне нужно было глубоко изучить хэш-функции. Вот некоторые вещи, которые я нашел.

Вы хотите, чтобы хеш равномерно и случайно распределял попадания по всему целевому пространству (обычно 32 бита, но может быть 16 или 64 бита). Вы хотите, чтобы каждый символ ввода имел одинаково большой эффект на выходе ,

ВСЕ простые хеши (например, ELF или PJW), которые просто перебирают строку и xor в каждом байте со сдвигом или модом, не пройдут по этим критериям по простой причине: последние добавленные символы имеют наибольшее влияние.

Но в Delphi и asm есть несколько действительно хороших алгоритмов. Вот несколько ссылок:

См. Статью доктора Доббса в 1997 году на burtleburtle.net/bob/hash/doobs.html
. код на burtleburtle.net/bob/c/lookup3.c

Функция SuperFastHash c2004-2008 Пола Се (AKA HsiehHash)
www.azillionmonkeys.com/qed/hash.html

Исходный код Delphi (с дополнительным asm) вы найдете по этой ссылке:
http://landman -code.blogspot.com / 2008/06 / superfasthash-из-Поль-hsieh.html
13 июля 2008 года
«Более года назад Юхани Сухонен попросил быстрый хеш, чтобы использовать его хеш-таблица. Я предложил старый, но хорошо исполняемый эльфийский хэш, но также отметил намного лучшая хеш-функция, которую я недавно нашел. Это называлось SuperFastHash (SFH) и был создан Полом Се, чтобы преодолеть его «проблемы» с хэш-функциями от Боба Дженкинса. Юхани спросил, может ли кто-нибудь написать функцию SFH в basm. Несколько человек работали над базовой реализацией и опубликовали ее. "

Хеширующая сага продолжается:
2007-03-13 Эндрю: Когда плохое хеширование означает хорошее кэширование
www.team5150.com/~andrew/blog/2007/03/hash_algorithm_attacks.html
2007-03-29 Эндрю: Взлом SuperFastHash
floodyberry.wordpress.com/2007/03/29/breaking-superfasthash/
2008-03-03 Остин Эпплби: MurmurHash 2.0
murmurhash.googlepages.com/
SuperFastHash - 985,335173 МБ / с
lookup3 - 988.080652 МБ / с
MurmurHash 2.0 - 2056,885653 МБ / с
Поставляет код c ++ MurmurrHash2.cpp и выровненную реализацию только для чтения -
MurmurHashAligned2.cpp
// ================================================ ========================
// Вот MurmurHash2 Landman's в C #
// 2009-02-25 Дэви Лэндман делает C # импликации SuperFashHash и MurmurHash2
//landman-code.blogspot.com/search?updated-min=2009-01-01T00%3A00%3A00%2B01%3A00&updated-max=2010-01-01T00%3A00%3A00%2B01%3A00&max-results=2
//
// Landman реализует SuperFastHash и MurmurHash2 4 способами в C #:
// 1: управляемый код 2: встроенный битовый преобразователь 3: Int Hack 4: небезопасные указатели
// SuperFastHash 1: 281 2: 780 3: 1204 4: 1308 МБ / с
// MurmurHash2 1: 486 2: 759 3: 1430 4: 2196

Извините, если вышесказанное выглядит как беспорядок. Я должен был просто вырезать и вставить его.

По крайней мере, одна из приведенных выше ссылок дает вам возможность получить 64-битный хеш, который наверняка не будет иметь коллизий в пространстве номеров кредитных карт и может быть легко сохранен в поле bigint в MySQL. 1047 *

Вам не нужен криптографический хеш. Они намного больше загружают процессор. И цель «криптографии» - остановить взлом, а не избежать столкновений.

2 голосов
/ 01 сентября 2009

Если производительность является фактором, я предлагаю взглянуть на запись CodeCentral Питера Внизу. Очень хорошо подходит для большого количества предметов.

По умолчанию используется P.J. Weinberger ELF хеш-функция . Но другие также предоставляются.

1 голос
/ 02 сентября 2009

Наилучшая хеш-функция для натуральных чисел let

 f(n)=n

Нет конфликтов;)

1 голос
/ 01 сентября 2009

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

Как очень быстрая альтернатива, я также использовал этот алгоритм в течение нескольких лет и у меня было мало проблем со столкновениями, однако я не могу дать вам математический анализ его присущей надежности, но для чего он стоит здесь, это

= Редактировать - Мой пример кода был неверным - теперь исправлен =

В к / с ++

unsigned int Hash(const char *s)
{
    int hash = 0;

    while (*s != 0)
    {
        hash *= 37;
            hash += *s;
        s++;
    }

    return hash;
}

Обратите внимание, что '37' - это магическое число, поэтому оно выбрано потому, что оно простое

1 голос
/ 01 сентября 2009

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

Поэтому я советую вам использовать любой криптографический хеш (например, SHA-256) с солью.

...