Если я правильно прочитал ваш вопрос, вы хотите сгенерировать токен произвольного идентификатора, который должен быть не более 21 символа. Должен ли он быть очень устойчивым к угадыванию? Пример, который вы привели, не является "криптографически сильным" в том смысле, что его можно угадать, выполнив поиск менее половины всего возможного пространства ключей.
Вы не говорите, могут ли символы быть всеми 256 символами ASCII или должны ли они быть ограничены, скажем, печатным ASCII (33-127 включительно) или некоторым меньшим диапазоном.
Существует модуль Python, разработанный для UUID s (уникальные идентификаторы Universals). Скорее всего, вам нужен uuid4, который генерирует случайный UUID и использует поддержку ОС, если она доступна (в Linux, Mac, FreeBSD и, возможно, в других).
>>> import uuid
>>> u = uuid.uuid4()
>>> u
UUID('d94303e7-1be4-49ef-92f2-472bc4b4286d')
>>> u.bytes
'\xd9C\x03\xe7\x1b\xe4I\xef\x92\xf2G+\xc4\xb4(m'
>>> len(u.bytes)
16
>>>
16 случайных байтов очень неубедительны, и нет необходимости использовать полные 21 байт, которые разрешает ваш API, если все, что вам нужно, это иметь неуязвимый непрозрачный идентификатор.
Если вы не можете использовать такие необработанные байты, что, вероятно, является плохой идеей, поскольку их труднее использовать в журналах и других сообщениях отладки, а также сложнее сравнивать на глаз, тогда преобразуйте байты во что-то более читаемое, например используя кодировку base-64, с результатом, сокращенным до 21 (или любого другого) байта:
>>> u.bytes.encode("base64")
'2UMD5xvkSe+S8kcrxLQobQ==\n'
>>> len(u.bytes.encode("base64"))
25
>>> u.bytes.encode("base64")[:21]
'2UMD5xvkSe+S8kcrxLQob'
>>>
Это дает вам высококачественную случайную строку длиной 21.
Вам может не понравиться '+' или '/', которые могут быть в строке base-64, поскольку без надлежащего экранирования это может помешать URL-адресам. Поскольку вы уже думаете об использовании «случайных 3 символов», я не думаю, что это ваше беспокойство. Если это так, вы можете заменить эти символы чем-то другим («-» и «.» Могут работать) или удалить их, если они есть.
Как уже отмечали другие, вы можете использовать .encode ("hex") и получить шестнадцатеричный эквивалент, но это всего 4 бита случайности / символа * Максимум 21 символ дает вам 84 бита случайности вместо вдвое больше. Каждый бит удваивает ваше пространство клавиш, делая теоретическое пространство поиска намного, намного меньше. В 2E24 раза меньше.
Ваше пространство клавиш по-прежнему имеет размер 2E24, даже с шестнадцатеричным кодированием, поэтому я думаю, что это скорее теоретическая проблема. Я бы не стал беспокоиться о том, что люди совершают грубые атаки на вашу систему.
Редактировать :
P.S .: Функция uuid.uuid4 использует libuuid, если доступно. Это получает свою энтропию от os.urandom (если доступно), иначе от текущего времени и локального MAC-адреса Ethernet. Если libuuid недоступен, то функция uuid.uuid4 получает байты непосредственно из os.urandom (если доступен), в противном случае она использует случайный модуль. Случайный модуль использует начальное значение по умолчанию, основанное на os.urandom (если доступно), в противном случае - значение, основанное на текущем времени. Зондирование выполняется для каждого вызова функции, поэтому если у вас нет os.urandom, тогда издержки немного больше, чем вы могли бы ожидать.
Принять домой сообщение? Если вы знаете, что у вас есть os.urandom, тогда вы можете сделать
os.urandom(16).encode("base64")[:21]
но если вы не хотите беспокоиться о его доступности, используйте модуль uuid.