Dans la construction d'un système basé sur Java qui nécessite des identifiants uniques sur l'URL, UUID.randomUUID()
ou SecureRandom
est-il un meilleur choix?
Plus précisément, un appel à POST /items
renverra un 201 Created
avec un emplacement au nouveau /items/{id}
où {id}
est une chaîne aléatoire et l'URL est destinée à être utilisée par des requêtes HTTP anonymes pour une fenêtre de temps limitée.
Ce code renvoie une chaîne de 22 caractères (par exemple B-KEPLFdWNZ4JTUnnEq3Og
) avec 128 bits de caractère aléatoire.
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[16];
random.nextBytes(bytes);
Encoder encoder = Base64.getUrlEncoder().withoutPadding();
String token = encoder.encodeToString(bytes);
System.out.println(token);
ID.randomUUID () est une "usine statique pour récupérer un UUID de type 4 (généré de manière pseudo aléatoire). L'UUID est généré à l'aide d'un générateur de nombres pseudo aléatoires cryptographiquement fort." Ce code renvoie une chaîne de 36 caractères (par exemple f0803ce7-8e3d-421a-8126-e5a0bd0a865f
) avec 122 bits de caractère aléatoire.
UUID randomUUID = UUID.randomUUID();
System.out.println(randomUUID);
Ces identificateurs seront également conservés dans une base de données relationnelle sous la forme d'un type CHAR
de longueur fixe.
En termes de quantité brute de bits aléatoires, oui.
En regardant la génération source de l'UUID aléatoire de Java , vous pouvez voir qu'ils utilisent réellement la classe SecureRandom
pour générer les octets bruts. Ainsi, la "qualité" de votre caractère aléatoire ne changera pas.
Le fait que vous ayez 6 bits de plus rend, techniquement, plus difficile de forcer brutalement un doublon. article de Wikipedia sur UUID a une bonne description des mathématiques derrière. Pour le citer:
[Après] avoir généré 1 milliard d'UUID par seconde au cours des 100 prochaines années, la probabilité de créer un seul doublon serait d'environ 50%
Si ces liens vont expirer dans, disons, 24 heures, c'est une très petite chose à se soucier, et ce n'est qu'avec 122 bits.
Maintenant, plus de bits vont-ils rendre plus difficile pour un attaquant de forcer brutalement une collision? Sûr! Est-ce que ça vaut le coup? Peut-être ... mais peut-être pas. Vous seul pouvez vraiment décider de ce qui convient à votre cas d'utilisation.
Lorsque vous arrivez au point où la brutalité forçant une collision prendrait, en moyenne, plus de temps que la mort thermique estimée de l'univers, cela pourrait être un peu exagéré. Cela revient à se soucier davantage des pirates qui compromettent la sécurité de votre stockage que du forçage brutal (et, honnêtement, même avec les UUID, vous allez avoir ce problème). En fin de compte, la difficulté du forçage brutal n'a pas d'importance s'ils peuvent trouver un autre moyen. Si vous êtes vraiment inquiet, le stockage est bon marché - stockez les chaînes de 256 bits que vous générez avec un SecureRandom
et appelez-le bien, mais assurez-vous d'avoir une sécurité appropriée pour tout le reste, sinon c'est inutile.
Eh bien, en principe, les UUID et les RNG cryptographiques promettent deux choses différentes:
Si # 2 est un problème, je m'assurerais d'utiliser un RNG cryptographique. Par exemple, si vous craignez qu'un attaquant malveillant qui peut demander beaucoup d'UUID dans un court laps de temps puisse apprendre suffisamment d'informations pour prédire celles données aux demandes des autres utilisateurs.
En l'occurrence, la méthode UUID.randomUUID()
de Java utilise un RNG cryptographique, elle devrait donc être sûre à cet égard. Mais le risque est alors que les développeurs qui regardent plus tard votre code ne comprennent pas que votre intention comprend un choix aléatoire cryptographique sécurisé, et pas seulement l'unicité. Par exemple, quelqu'un pourrait réécrire ce module particulier en utilisant un autre langage ou une bibliothèque UUID qui n'utilise pas de RNG cryptographique, et vous exposer à des attaques basées sur les prédictions que vous vouliez exclure. Donc, si la sécurité cryptographique était vraiment un facteur important, j'essaierais d'écrire le code d'une manière qui le rende évident, en utilisant explicitement SecureRandom
. (Le code critique pour la sécurité doit être très clair!)
Si tout ce qui m'intéressait était l'unicité au lieu de la sécurité, j'irais de l'avant et utiliser UUID.randomUUID()
.