2009-03-07 6 views
7

J'utilise une API qui prend un nom de 21 caractères maximum pour représenter une session interne qui a une durée de vie d'environ "deux jours". Je voudrais que le nom ne soit pas significatif en utilisant une sorte de possession? md5 génère 40 caractères, y a-t-il autre chose que je pourrais utiliser?Python et touches aléatoires de 21 caractères maximum

Pour l'instant j'utiliser 'userid [10]' + heure de création: ddhhmmss + 3 caractères aléatoires.

Merci,

+0

Vous devez penser SHA1. MD5 est de 32 chiffres hexadécimaux. – kmkaplan

Répondre

23

Si je lis bien votre question, vous voulez générer un certain jeton d'identification arbitraire qui doit être 21 caractères max. Est-ce qu'il doit être très résistant aux devinettes? L'exemple que vous avez donné n'est pas "crytographiquement fort" en ce sens qu'il peut être deviné en cherchant bien moins de la moitié de tout l'espace-clé possible. Vous ne dites pas si les caractères peuvent être tous les 256 caractères ASCII, ou si elle doit être limitée à, disons, ASCII imprimable (33-127, inclusivement), ou une plus petite portée.

Il y a un module Python conçu pour UUID s (universaux Unique Identifiers). Vous voulez probablement uuid4 qui génère un UUID aléatoire, et utilise le support de l'OS si disponible (sur Linux, Mac, FreeBSD, et probablement d'autres).

>>> 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 octets aléatoires est très unguessable, et il n'y a pas besoin d'utiliser les 21 octets complet de votre API permet, si tout ce que vous voulez est d'avoir un identifiant opaque unguessable. Si vous ne pouvez pas utiliser des octets bruts comme cela, ce qui est probablement une mauvaise idée car il est plus difficile à utiliser dans les journaux et les autres messages de débogage et plus difficile à comparer à l'oeil, convertir les octets en quelque chose de plus lisible. comme l'utilisation de base 64 codage, le résultat abattu à 21 (ou autre) octets:

>>> u.bytes.encode("base64") 
'2UMD5xvkSe+S8kcrxLQobQ==\n' 
>>> len(u.bytes.encode("base64")) 
25 
>>> u.bytes.encode("base64")[:21] 
'2UMD5xvkSe+S8kcrxLQob' 
>>> 

Cela vous donne une chaîne aléatoire de très haute qualité de longueur 21.

Vous pourriez ne pas aimer le '+' ou le '/' qui peut être dans une chaîne de base 64, car sans échappement approprié qui pourrait interférer avec les URL. Puisque vous pensez déjà à utiliser "3 caractères aléatoires", je ne pense pas que cela vous inquiète. Si c'est le cas, vous pourriez remplacer ces caractères par quelque chose d'autre ('-' et '.' Pourraient fonctionner), ou les supprimer s'ils sont présents. Comme d'autres l'ont souligné, vous pouvez utiliser .encode ("hex") et obtenir l'équivalent hexadécimal, mais c'est seulement 4 bits de caractère aléatoire/caractère * 21 caractères max vous donne 84 bits de hasard au lieu de deux fois cela. Chaque bit double votre espace de touches, ce qui rend l'espace de recherche théorique beaucoup plus petit. Par un facteur de 2E24 plus petit.

Votre espace de clés est toujours de taille 2E24, même avec un codage hexadécimal, donc je pense que c'est plus une préoccupation théorique. Je ne m'inquiéterais pas des gens qui font des attaques par force brute contre votre système.

Modifier:

P.S .: La fonction uuid.uuid4 utilise libuuid si elle est disponible. Cela obtient son entropie de os.urandom (si disponible) sinon de l'heure actuelle et l'adresse MAC locale ethernet. Si libuuid n'est pas disponible, la fonction uuid.uuid4 récupère directement les octets de os.urandom (si disponible) sinon elle utilise le module random. Le module random utilise un seed par défaut basé sur os.urandom (si disponible) sinon une valeur basée sur l'heure actuelle. Le sondage a lieu pour chaque appel de fonction, donc si vous n'avez pas os.urandom alors le surcoût est un peu plus grand que ce à quoi vous pourriez vous attendre.

Envoyer un message à la maison? Si vous savez que vous avez os.urandom alors vous pourriez faire

os.urandom(16).encode("base64")[:21] 

mais si vous ne voulez pas vous soucier de sa disponibilité utiliser le module UUID.

+0

J'ai oublié d'ajouter que devrait être url sûr, je devrais l'avoir spécifié dans les trois caractères aléatoires. Je vais utiliser votre méthode et remplacer les caractères + et//. – coulix

+0

J'ai trouvé une méthode sûre uri_b64encode qui ferait bien le travail merci – coulix

+0

Notez que UUID4 ne vous donne pas exactement 16 octets aléatoires. Il y a 6 bits fixes (non aléatoires). Bien sûr, cela suffit amplement. – kmkaplan

2

Pourquoi ne pas prendre 21 premiers caractères de md5 ou SHA1?

+0

Vrai cela devrait être assez aléatoire – coulix

+0

Quelque chose comme hashlib.md5 (str (random.random())). Hexdigest() [: 21] –

+0

random.random() par défaut obtient sa graine de os.urandom, sinon de temps temps. En supposant que le système d'exploitation supporte os.urandom, on pourrait aussi faire os.urandom (11) .encode ("hex") [: 21]. –

4

La représentation hexadécimale de MD5 a très mauvais caractère aléatoire: vous obtenez seulement 4 bits d'entropie par caractère.

Utiliser des caractères aléatoires, quelque chose comme:

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

Dans le choix de mettre tous les caractères acceptables. Si vous utilisez une fonction de hachage réelle telle que SHA1, vous obtiendrez également de bons résultats si elle est utilisée correctement, la complexité ajoutée et la consommation CPU ne semble pas justifiée pour vos besoins. Vous voulez seulement une chaîne aléatoire.

+0

string.ascii_letters, car string.letters dépend des paramètres régionaux. –

0

Caractères ou octets? S'il prend des chaînes arbitraires, vous pouvez simplement utiliser les octets et ne pas vous soucier de l'expansion en caractères lisibles (pour qui base64 serait mieux que hexadécimal de toute façon).

MD5 génère 16 caractères si vous n'utilisez pas l'expansion hexadécimal de celui-ci. SHA1 génère 20 dans la même condition.

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

Peu d'octets supplémentaires sont nécessaires après cela.

2

Le module base64 peut effectuer un codage sécurisé pour les URL. Donc, si besoin, au lieu de

u.bytes.encode("base64") 

vous pourriez faire

import base64 

token = base64.urlsafe_b64encode(u.bytes) 

et, idéalement, pour reconvertir

u = uuid.UUID(bytes=base64.urlsafe_b64decode(token))