Jak utworzyć HashCode in.net (c#) Dla ciągu, który można bezpiecznie przechowywać w bazie danych?

Cytat z wytycznych i zasad GetHashCode autorstwa Erica Lipperta:

Reguła: konsumenci GetHashCode nie mogą polegać na tym, że jest stabilny w czasie lub w różnych aplikacjach

Załóżmy, że masz obiekt klienta która ma kilka pól takich jak nazwa, Adres, i tak dalej. Jeśli zrobisz dwa takich obiektów o dokładnie takim samym danych w dwóch różnych procesach, oni nie musisz zwracać tego samego hasha kod. Jeśli zrobisz taki obiekt na Wtorek w jednym procesie, wyłącz go, i uruchomić program ponownie na Środa, kody hash mogą być inaczej.

To ugryzło ludzi w przeszłości. Dokumentacja dla System.Sznurek.GetHashCode notes konkretnie, że dwa identyczne ciągi mogą mieć różne kody hash w różnych wersjach CLR oraz w rzeczywistości tak. nie przechowuj hashów ciągów w bazach danych i oczekuj, że będą zawsze takie same, ponieważ nie będą.

So what czy prawidłowy sposób tworzenia HashCode łańcucha znaków, który mogę przechowywać w bazie danych?

(proszę mi powiedzieć, że nie jestem pierwszą osobą, która zostawiła ten błąd w oprogramowaniu, które napisałem!)

Author: Eric Lippert, 2011-03-01

3 answers

To zależy, jakie właściwości chcesz, aby ten hash miał. Na przykład, Można po prostu napisać coś takiego:

public int HashString(string text)
{
    // TODO: Determine nullity policy.

    unchecked
    {
        int hash = 23;
        foreach (char c in text)
        {
            hash = hash * 31 + c;
        }
        return hash;
    }
}

Tak długo, jak dokument że tak jest obliczany hash, jest to ważne. To nie jest w żaden sposób kryptograficznie bezpieczne lub coś w tym stylu, ale można utrzymać go bez problemów. Dwa ciągi, które są bezwzględnie równe w sensie porządkowym (tzn. bez równości kulturowej itp., dokładnie znak po znaku taki sam), otrzymają ten sam hash z tym kodem.

Problemy pojawiają się, gdy polegasz na nieudokumentowanym hashowaniu - tzn. czymś, co spełnia GetHashCode(), ale w żaden sposób nie gwarantuje, że pozostanie takie samo od wersji do wersji... jak string.GetHashCode().

Pisanie i dokumentowanie własnego hasha w ten sposób jest trochę jak powiedzenie, " te poufne informacje są zaszyfrowane MD5 (lub cokolwiek innego)". Tak długo, jak jest to dobrze zdefiniowany hash, to w porządku.

EDIT: Inne odpowiedzi sugerowały użycie skrótów kryptograficznych, takich jak SHA-1 lub MD5. Powiedziałbym, że dopóki nie dowiemy się, że istnieje wymóg bezpieczeństwa kryptograficznego, a nie tylko stabilności, nie ma sensu przechodzić przez rigmarole konwersji łańcucha na tablicę bajtów i hashowania tego. Oczywiście, jeśli hash jest przeznaczony do wszystkiego, co związane z bezpieczeństwem, standardowy hash branżowy jest Dokładnie , Po co powinieneś sięgać. Ale to nie było nigdzie wspomniane w pytaniu.

 66
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-01 13:25:57

Oto reimplementacja obecny sposób, w jaki.NET oblicza kod hashowy dla 64-bitowych systemów. To nie używa wskaźników, jak robi to prawdziwe GetHashCode(), więc będzie nieco wolniejsze, ale czyni go bardziej odpornym na wewnętrzne zmiany w string, to da bardziej równomiernie rozłożony kod hash niż wersja Jona Skeeta , co może skutkować lepszymi czasami wyszukiwania w słownikach.

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);
        }
    }
}
 6
Author: Scott Chamberlain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-06 19:08:23

Odpowiedzią jest po prostu napisanie własnej funkcji hashującej. Możesz znaleźć źródło dla niektórych, klikając linki w komentarzach do opublikowanego artykułu. Możesz też użyć wbudowanej funkcji skrótu, która pierwotnie była przeznaczona do kryptografii (MD5, SHA1 itp.) i po prostu nie używać wszystkich bitów.

 1
Author: Gabe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-01 13:18:02