web-dev-qa-db-fra.com

Collisions lors de la génération d'UUID en JavaScript?

Cela concerne cette question . J'utilise cette réponse pour générer l'UUID en JavaScript:

'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
    return v.toString(16);
});

Cette solution semble bien fonctionner, mais je reçois des collisions. Voici ce que j'ai:

  • Une application Web fonctionnant dans Google Chrome.
  • 16 utilisateurs.
  • environ 4 000 UUID ont été générés au cours des 2 derniers mois par ces utilisateurs.
  • j'ai eu environ 20 collisions - par exemple le nouvel UUID généré aujourd'hui était le même qu'il y a environ 2 mois (utilisateur différent).

Les questions sont donc:

  1. Quelle est la cause du problème?
  2. Comment puis-je l'éviter?
90
Muxa

Ma meilleure supposition est que Math.random() est cassé sur votre système pour une raison quelconque (aussi bizarre que cela puisse paraître). Ceci est le premier rapport que j'ai vu de quiconque ayant des collisions.

node-uuid A un test harnais que vous pouvez utiliser pour tester la distribution des chiffres hexadécimaux dans ce code. Si cela semble correct, ce n'est pas Math.random(), alors essayez de remplacer l'implémentation UUID que vous utilisez dans la méthode uuid() et voyez si vous obtenez toujours de bons résultats.

[Mise à jour: Je viens de voir le rapport de Veselin sur le bogue avec Math.random() au démarrage. Étant donné que le problème n'est qu'au démarrage, le test node-uuid Est peu susceptible d'être utile. Je commenterai plus en détail le lien devoluk.com.]

33
broofa

En effet il y a des collisions mais uniquement sous Google Chrome. Découvrez mon expérience sur le sujet ici

http://devoluk.com/google-chrome-math-random-issue.html

Il semble que les collisions ne se produisent que lors des premiers appels de Math.random. Parce que si vous exécutez simplement la méthode createGUID/testGUIDs ci-dessus (qui était évidemment la première chose que j'ai essayée), cela fonctionne sans aucune collision.

Donc, pour faire un test complet, il faut redémarrer Google Chrome, générer 32 octets, redémarrer Chrome, générer, redémarrer, générer ...

35
Veselin Kulov

Juste pour que d'autres personnes soient au courant de cela - je rencontrais un nombre étonnamment élevé de collisions apparentes en utilisant la technique de génération d'UUID mentionnée ici. Ces collisions ont continué même après que je suis passé à seedrandom pour mon générateur de nombres aléatoires. Cela m'a fait arracher mes cheveux, comme vous pouvez l'imaginer.

J'ai finalement compris que le problème était (presque?) Exclusivement associé aux robots d'exploration de Google. Dès que j'ai commencé à ignorer les demandes avec "googlebot" dans le champ user-agent, les collisions ont disparu. Je suppose qu'ils doivent mettre en cache les résultats des scripts JS d'une manière semi-intelligente, avec pour résultat final que leur navigateur araignée ne peut pas être compté pour se comporter comme les navigateurs normaux.

Juste un FYI.

18
Ken Smith

Je voulais poster ceci en tant que commentaire à votre question, mais apparemment StackOverflow ne me le permettra pas.

Je viens de lancer un test rudimentaire de 100 000 itérations en Chrome en utilisant l'algorithme UUID que vous avez publié et sans collision. Voici un extrait de code:

var createGUID = function() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
}

var testGUIDs = function(upperlimit) {
    alert('Doing collision test on ' + upperlimit + ' GUID creations.');
    var i=0, guids=[];
    while (i++<upperlimit) {
        var guid=createGUID();
        if (guids.indexOf(guid)!=-1) {
            alert('Collision with ' + guid + ' after ' + i + ' iterations');
        }
        guids.Push(guid);
    }
    alert(guids.length + ' iterations completed.');
}

testGUIDs(100000);

Êtes-vous sûr qu'il ne se passe rien d'autre ici?

4
user533676

La réponse qui a initialement publié cette solution UUID a été mise à jour le 2017-06-28:

Un bon article de développeurs Chrome discutant de l'état de la qualité Math.random PRNG dans Chrome, Firefox et Safari. tl; dr - À la fin de 2015, c'est "assez bien", mais pas la qualité cryptographique. Pour résoudre ce problème, voici une version mise à jour de la solution ci-dessus qui utilise ES6, l'API crypto et un peu de JS wizardy dont je ne peux pas me féliciter :

function uuidv4() {
  return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
    (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
  )
}

console.log(uuidv4());
3
Luke M Willis

Les réponses ici concernent "qu'est-ce qui cause le problème?" (Problème de graine Chrome Math.random) mais pas "comment puis-je l'éviter?".

Si vous cherchez toujours comment éviter ce problème, j'ai écrit cette réponse il y a quelque temps comme une version modifiée de la fonction de Broofa pour contourner ce problème exact. Cela fonctionne en compensant les 13 premiers nombres hexadécimaux par une partie hexadécimale de l'horodatage, ce qui signifie que même si Math.random est sur la même graine, il générera toujours un UUID différent à moins qu'il ne soit généré exactement à la même milliseconde.

0
Briguy37