Подтвердить что ты не робот

Нужна меньшая альтернатива GUID для идентификатора базы данных, но по-прежнему уникальная и случайная для URL-адреса

Я искал все это для этого, и я не могу получить полный ответ для этого. Поэтому, если ответ уже существует в stackoverflow, я заранее извиняюсь.

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

Из моего поиска я понимаю, что мне бы хотелось избежать столкновений, и я прочитал несколько упоминаний о SHA1.

Мои основные требования:

  • Что-то меньшее, чем GUID. (Выглядит ужасно по URL-адресу)
  • Должно быть уникальным
  • Избегайте столкновений
  • Не длинный список странных символов, которые не читаются.

Примером того, что я ищу, будет www.somesite.com/page.aspx?id=AF78FEB

Я не уверен, должен ли я реализовывать это в базе данных (я использую SQL Server 2005) или в коде (я использую С# ASP.Net)

EDIT:

Из всего прочитанного я понял, что это безопасность через неясность. Я имею намерение иметь правильную авторизацию и аутентификацию для доступа к страницам. Я буду использовать .Net-аутентификацию и авторизацию. Но как только законный пользователь зарегистрировался и получает доступ к готовой (но динамически созданной странице), заполненной ссылками на принадлежащие ему предметы. Например, ссылка может быть www.site.com/page.aspx?item_id=123. Что мешает ему щелкнуть по этой ссылке, а затем изменить URL-адрес выше, чтобы перейти на сайт www.site.com/page.aspx?item_id=456, который НЕ принадлежит ему? Я знаю некоторые Java-технологии, такие как Struts (я должен быть исправлен) хранить все в сеансе и как-то работать с этим, но я понятия не имею, как это делается.

4b9b3361

Ответ 1

[В ответ на изменение]
Вы должны рассматривать строки запроса как "неправильный ввод". Вам необходимо программно проверить, что аутентифицированный пользователь может просмотреть запрошенный элемент.

if( !item456.BelongsTo(user123) )
{
  // Either show them one of their items or a show an error message.
}

Ответ 2

Раймонд Чен содержит хорошую статью о том, почему вы не должны использовать "половину руководства" и предлагает подходящее решение для создания вашего собственное значение "не совсем ориентировочно, но достаточно хорошее" здесь:

GUID глобально уникальны, но подстроки GUID не являются

Его стратегия (без конкретной реализации) основывалась на:

  • Четыре бита для кодирования номера компьютера,
  • 56 бит для метки времени и
  • четыре бита в качестве uniquifier.

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

Вы можете уйти с четырехбитным uniquifier, предположив, что часы не будут дрейфовать более часа из-за перекоса (скажем) и что часы не будут reset более шестнадцати раз в час.

Ответ 3

ОБНОВЛЕНИЕ (4 февраля 2017 г.):
Walter Stabosz обнаружил ошибку в исходном коде. При расследовании были обнаружены дополнительные ошибки, однако обширное тестирование и переработка кода я сам, автор оригинала (CraigTP) исправил все эти проблемы. Я обновил здесь код с правильной рабочей версией, и вы также можете скачать решение для Visual Studio 2015 здесь, которое содержит "короткий код", код генерации и довольно полный набор тестов для подтверждения правильности.

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

Пример

Console.WriteLine($"1371 as a shortcode is: {ShortCodes.LongToShortCode(1371)}");
Console.WriteLine($"12345 as a shortcode is: {ShortCodes.LongToShortCode(12345)}");
Console.WriteLine($"7422822196733609484 as a shortcode is: {ShortCodes.LongToShortCode(7422822196733609484)}");

Console.WriteLine($"abc as a long is: {ShortCodes.ShortCodeToLong("abc")}");
Console.WriteLine($"ir6 as a long is: {ShortCodes.ShortCodeToLong("ir6")}");
Console.WriteLine($"atnhb4evqqcyx as a long is: {ShortCodes.ShortCodeToLong("atnhb4evqqcyx")}");    

// PLh7lX5fsEKqLgMrI9zCIA   
Console.WriteLine(GuidToShortGuid( Guid.Parse("957bb83c-5f7e-42b0-aa2e-032b23dcc220") ) );      

Код

В следующем коде показан простой класс, который изменит длину на "код" (и обратно!):

public static class ShortCodes
{
    // You may change the "shortcode_Keyspace" variable to contain as many or as few characters as you
    // please.  The more characters that are included in the "shortcode_Keyspace" constant, the shorter
    // the codes you can produce for a given long.
    private static string shortcodeKeyspace = "abcdefghijklmnopqrstuvwxyz0123456789";

    public static string LongToShortCode(long number)
    {
        // Guard clause.  If passed 0 as input
        // we always return empty string.
        if (number == 0)
        {
            return string.Empty;
        }

        var keyspaceLength = shortcodeKeyspace.Length;
        var shortcodeResult = "";
        var numberToEncode = number;
        var i = 0;
        do
        {
            i++;
            var characterValue = numberToEncode % keyspaceLength == 0 ? keyspaceLength : numberToEncode % keyspaceLength;
            var indexer = (int) characterValue - 1;
            shortcodeResult = shortcodeKeyspace[indexer] + shortcodeResult;
            numberToEncode = ((numberToEncode - characterValue) / keyspaceLength);
        }
        while (numberToEncode != 0);
        return shortcodeResult;
    }

    public static long ShortCodeToLong(string shortcode)
    {
        var keyspaceLength = shortcodeKeyspace.Length;
        long shortcodeResult = 0;
        var shortcodeLength = shortcode.Length;
        var codeToDecode = shortcode;
        foreach (var character in codeToDecode)
        {
            shortcodeLength--;
            var codeChar = character;
            var codeCharIndex = shortcodeKeyspace.IndexOf(codeChar);
            if (codeCharIndex < 0)
            {
                // The character is not part of the keyspace and so entire shortcode is invalid.
                return 0;
            }
            try
            {
                checked
                {
                    shortcodeResult += (codeCharIndex + 1) * (long) (Math.Pow(keyspaceLength, shortcodeLength));
                }
            }
            catch(OverflowException)
            {
                // We've overflowed the maximum size for a long (possibly the shortcode is invalid or too long).
                return 0;
            }
        }
        return shortcodeResult;
    }
}

}

Это, по сути, ваша собственная система нумерации baseX (где X - количество уникальных символов в константе shortCode_Keyspace.

Чтобы сделать вещи непредсказуемыми, начните свою внутреннюю инкрементную нумерацию с чего-то другого, кроме 1 или 0 (т.е. начните с 184723), а также измените порядок символов в константе shortCode_Keyspace (например, используйте буквы AZ и цифры 0-9, но обманывают их порядок в константной строке. Это поможет сделать каждый код несколько непредсказуемым.

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

EDIT: Если вы просто хотите сгенерировать GUID и преобразовать его в нечто уникальное, но содержит несколько меньших символов, эта небольшая функция сделает трюк:

public static string GuidToShortGuid(Guid gooid)
{
    string encoded = Convert.ToBase64String(gooid.ToByteArray());
    encoded = encoded.Replace("/", "_").Replace("+", "-");
    return encoded.Substring(0, 22);
}

Ответ 4

Если вы не хотите, чтобы другие пользователи видели информацию о людях, почему бы вам не защитить страницу, на которой вы используете идентификатор?

Если вы это сделаете, то не имеет значения, используете ли вы инкрементирующий идентификатор.

Ответ 5

Вы можете произвольно сгенерировать число. Убедитесь, что этот номер еще не находится в БД и его использовать. Если вы хотите, чтобы он отображался как случайная строка, вы можете просто преобразовать его в шестнадцатеричный, так что вы получите A-F там, как в вашем примере.

Ответ 6

GUID - 128 бит. Если вы берете эти биты и не используете набор символов с 16 символами для их представления (16 = 2 ^ 4 и 128/4 = 32 символа), но набор символов, допустим, 64 символа (например, Base 64), вы будет иметь только 22 символа (64 = 2 ^ 6 и 128/6 = 21.333, поэтому 22 символа).

Ответ 7

Сделайте свой идентификатор автоматического увеличения, а HMAC-SHA1 - секретом, известным только вам. Это создаст случайные 160-битные, которые скрывают реальный инкрементный ID. Затем возьмите префикс длины, который делает конфликты достаточно маловероятными для вашего приложения - скажем, 64-битные, которые вы можете кодировать в 8 символов. Используйте это как свою строку.

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

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

Ответ 8

Как долго это слишком долго? Вы можете преобразовать GUID в Base 64, что в конечном итоге делает его немного короче.

Ответ 9

Что бы вы могли сделать, это то, что я делаю, когда хочу точно, чего вы хотите.

  • Создайте свой GUID.

  • Удалите тире и получите подстрока того, как долго вы хотите ID

  • Проверьте db для этого идентификатора, если он существует goto шаг 1.

  • Вставить запись.

Это самый простой способ гарантировать, что он затенен и уникален.

Ответ 10

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

Ответ 11

GUID - это всего лишь число

Новое поколение GUID (версия 4) - это в основном большое случайное число *

Поскольку это большое случайное число, вероятность столкновения ДЕЙСТВИТЕЛЬНО мала.

Самое большое число, которое вы можете сделать с помощью GUID, закончилось:

5,000,000,000,000,000,000,000,000,000,000,000,000

Итак, если вы создадите два идентификатора GUID, второй идентификатор GUID будет таким же, как и первый:

1 in 5,000,000,000,000,000,000,000,000,000,000,000,000

Если вы создаете 100 МЛРД. GUID.

Вероятность того, что ваш 100-миллиардный GUID столкнется с другими 99,999,999,999 GUID:

1 in 50,000,000,000,000,000,000,000,000

Почему 128 бит?

Одна из причин заключается в том, что компьютеры работают с краткими 8 битами.

8, 16, 32, 64, 128 и т.д.

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

Вам нужны 128 бит?

Нет, сколько бит вам нужно, зависит от того, сколько чисел вы ожидаете генерировать и насколько уверены, что хотите, чтобы они не сталкивались.

64-разрядный пример

Тогда вероятность того, что ваше второе число столкнется с первым, будет:

1 in 18,000,000,000,000,000,000 (64 bit)

Вместо:

1 in 5,000,000,000,000,000,000,000,000,000,000,000,000 (128 bit)

Как насчет 100-миллиардного числа?

Вероятность того, что ваш 100-миллиардный номер столкнется с другими 99,999,999,999, будет:

1 in 180,000,000 (64 bit)

Вместо:

1 in 50,000,000,000,000,000,000,000,000 (128 bit)

Значит, вы должны использовать 64 бита?

Зависит ли вы генерируете 100 миллиардов номеров? Даже если вы были тогда 180 000 000, вам неудобно?

Подробнее о GUID

Я специально говорю о версии 4.

Версия 4 фактически не использует все 128 бит для части случайного числа, она использует 122 бита. Остальные 6 бит используются для указания, что это версия 4 стандарта GUID.

Числа в этом ответе основаны на 122 бит.

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

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

Вероятно, он использовал генератор случайных чисел, который поставляется с операционной системой.