Python и случайные ключи из 21 символа макс - PullRequest
7 голосов
/ 07 марта 2009

Я использую API, имя которого равно 21 символу max, для представления внутреннего сеанса с временем жизни около «двух дней». Хотелось бы, чтобы название не имело смысла при использовании какого-то хасинга? md5 генерирует 40 символов, могу ли я использовать что-то еще?

Пока я использую 'userid [: 10]' + время создания: ddhhmmss + случайные 3 символа.

Спасибо

Ответы [ 5 ]

23 голосов
/ 07 марта 2009

Если я правильно прочитал ваш вопрос, вы хотите сгенерировать токен произвольного идентификатора, который должен быть не более 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.

4 голосов
/ 07 марта 2009

Шестнадцатеричное представление MD5 имеет очень слабую случайность: вы получаете только 4 бита энтропии на символ.

Используйте случайные символы, что-то вроде:

import random
import string
"".join([random.choice(string.ascii_letters + string.digits + ".-")
        for i in xrange(21)])

На выбор поставьте все допустимые символы.

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

2 голосов
/ 14 октября 2009

Модуль base64 может выполнять URL-безопасное кодирование. Поэтому при необходимости вместо

u.bytes.encode("base64")

вы могли бы сделать

import base64

token = base64.urlsafe_b64encode(u.bytes)

и, удобно, конвертировать обратно

u = uuid.UUID(bytes=base64.urlsafe_b64decode(token))
2 голосов
/ 07 марта 2009

Почему бы не взять первые 21 символ из хеша md5 или SHA1?

0 голосов
/ 07 марта 2009

Символы или байты? Если он принимает произвольные строки, вы можете просто использовать байты и не беспокоиться о расширении до читаемых символов (для которых base64 в любом случае будет лучше, чем шестнадцатеричный).

MD5 генерирует 16 символов, если вы не используете его шестнадцатеричное расширение. SHA1 генерирует 20 при том же условии.

>>> import hashlib
>>> len(hashlib.md5('foobar').digest())
16
>>> len(hashlib.sha1('foobar').digest())
20

После этого требуется несколько дополнительных байтов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...