Создание уникальных токенов, которые невозможно угадать - PullRequest
3 голосов
/ 27 января 2011

У меня есть система, которая должна планировать некоторые вещи и возвращать идентификаторы запланированным задачам некоторым сторонним объектам. Пользователь в основном сделает это:

identifier = MyLib.Schedule(something)
# Nah, let's unschedule it.
MyLib.Unschedule(identifier)

Я часто использую этот тип шаблона во внутреннем коде, и я всегда использую простые целые числа в качестве идентификатора. Но если идентификаторы используются ненадежным кодом, злоумышленник может взломать всю систему, выполнив один Unschedule(randint()).

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

Единственное решение, которое я могу придумать, - это генерировать 64-битные случайные числа в качестве идентификаторов и отслеживать, какие идентификаторы в настоящее время раздаются, чтобы избежать смехотворно маловероятных дубликатов. Или 128-битный? Когда я могу сказать «это достаточно случайно, дубликаты не могут возникнуть», если вообще когда-либо?

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

РЕДАКТИРОВАТЬ - Решение на основе принятого ответа:

from Crypto.Cipher import AES
import struct, os, itertools

class AES_UniqueIdentifier(object):
    def __init__(self):
        self.salt = os.urandom(8)
        self.count = itertools.count(0)
        self.cipher = AES.new(os.urandom(16), AES.MODE_ECB)
    def Generate(self):
        return self.cipher.encrypt(self.salt + 
                                   struct.pack("Q", next(self.count)))
    def Verify(self, identifier):
        "Return true if identifier was generated by this object."
        return self.cipher.decrypt(identifier)[0:8] == self.salt

Ответы [ 5 ]

3 голосов
/ 27 января 2011

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

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

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

1 голос
/ 28 января 2011

Для меня это звучит так, как будто вы слишком задумывались над этой проблемой.Это на 100% похоже на приложение для GUID / UUID .Python даже имеет встроенный способ генерировать их .Весь смысл GUID / UUID состоит в том, что вероятность столкновения является астрономической, и, используя строку вместо зашифрованного токена, вы можете пропустить операцию дешифрования на этапе проверки.Я думаю, что это также устранит целый ряд проблем, с которыми вы можете столкнуться при управлении ключами, и увеличит скорость всего процесса.

РЕДАКТИРОВАТЬ:

с UUIDВаш метод проверки будет просто сравнение между данным UUID и сохраненным.Поскольку вероятность столкновения между двумя UUID невероятно мала, вам не нужно беспокоиться о ложных срабатываниях.В вашем примере кажется, что один и тот же объект выполняет шифрование и дешифрование, при этом третьи лица не читают сохраненные данные.Если это так, вы ничего не получаете, передавая зашифрованные данные, за исключением того, что биты, которые вы передаете, нелегко угадать.Я думаю, что UUID даст вам те же преимущества, без затрат на операции шифрования.

0 голосов
/ 27 января 2011

Вам нужен этот шаблон в распределенной или локальной среде?

Если вы локальный, большинство ОО-языков должны поддерживать понятие идентичности объекта, поэтому, если вы создаете непрозрачный дескриптор - просто создайте новыйobject.

handle = new Object(); // in Java

Ни один другой клиент не может подделать это.

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

0 голосов
/ 27 января 2011

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

0 голосов
/ 27 января 2011

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

Посмотрите, как генерируются идентификаторы сеансов.Вот содержимое типичного куки-файла PHPSESSID:

bf597801be237aa8531058dab94a08a9

Если вы хотите быть мертвым, не зная никакой атаки методом перебора, сделайте вычисления в обратном направлении: Сколько попыток может сделать взломщик в секунду?Сколько разных уникальных идентификаторов используется в произвольный момент времени?Сколько всего идентификаторов?Сколько времени потребуется взломщику, чтобы покрыть, скажем, 1% от общего количества идентификаторов?Отрегулируйте количество бит соответственно.

...