JavaScript'te UUID'ler oluştururken meydana gelen çarpışmalar?


95

Bu, bu soruyla ilgilidir . JavaScript'te UUID oluşturmak için bu yanıttan aşağıdaki kodu kullanıyorum :

'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);
});

Bu çözüm iyi çalışıyor gibi görünüyordu, ancak çarpışmalarla karşılaşıyorum. İşte sahip olduğum şey:

  • Google Chrome'da çalışan bir web uygulaması.
  • 16 kullanıcı.
  • Bu kullanıcılar tarafından son 2 ayda yaklaşık 4000 UUID oluşturulmuştur.
  • Yaklaşık 20 çarpışma aldım - örneğin bugün oluşturulan yeni UUID yaklaşık 2 ay önceki ile aynıydı (farklı kullanıcı).

Bu soruna ne sebep oluyor ve bundan nasıl kaçınabilirim?


2
İyi bir rastgele sayıyı şu anki zamanla (milisaniye cinsinden) birleştirin. Rastgele sayının tam olarak aynı anda çarpışması ihtimali gerçekten, gerçekten, gerçekten düşüktür.
jfriend00

7
@ jfriend00 bunu yapmanız gerekiyorsa, o zaman bu "iyi bir rastgele sayı" değildir, hatta düzgün bir sözde rastgele sayı bile değildir.
Attila O.

2
(r&0x3|0x8)kısım / değerlendirme ne anlama geliyor?
Kristian

Bir Date.now (). ToString () eklemeye ne dersiniz?
Vitim.us

4
Mimarinizde UUID'lerle ilgisi olmayan büyük bir sorun var - istemci kasıtlı olarak çakışan kimlikler oluşturabilir. Kimlikleri yalnızca güvendiğiniz bir sistemle oluşturun. Bir çözüm olarak, istemci tarafından oluşturulan kimlikleri user_id ile başa ekleyin, böylece rakip / hatalı istemci yalnızca kendileriyle çarpışabilir (ve bunu sunucu tarafında halledebilir).
Dzmitry Lazerka

Yanıtlar:


36

En iyi tahminim, Math.random()herhangi bir nedenle sisteminizde bozuk (kulağa tuhaf geliyor). Bu, birinin çarpıştığını gördüğüm ilk rapordur.

node-uuidbu koddaki onaltılık basamakların dağılımını test etmek için kullanabileceğiniz bir test koşum takımı vardır. Bu iyi görünüyorsa o zaman değil Math.random(), o zaman kullandığınız UUID uygulamasını uuid()oradaki yönteme koymayı deneyin ve hala iyi sonuçlar alıp almadığınızı görün.

[Güncelleme: Veselin'inMath.random() başlangıçtaki hata hakkındaki raporunu az önce gördüm . Sorun sadece başlangıçta olduğundan, node-uuidtestin yararlı olma ihtimali düşüktür. Devoluk.com bağlantısıyla ilgili daha ayrıntılı yorum yapacağım.]


1
Teşekkürler, şimdi uuid.js ile gidiyorum, çünkü mevcutsa tarayıcının güçlü kriptosunu kullanıyor. Herhangi bir çarpışma olup olmadığını göreceğiz.
Muxa

Bahsettiğiniz uuid.js koduna bir bağlantı sağlayabilir misiniz? (üzgün, emin değilim ne demek Lib hangi.)
broofa

10
Şimdiye kadar hiç çarpışma olmadı :)
Muxa

Her neyse, Chrome ise ve yalnızca başlarken, uygulamanız yukarıdaki işlevi kullanarak örneğin on kılavuzdan oluşan bir sıra oluşturabilir ve silebilir :)
Vinko Vrsalovic

Problem Math.random () 'dan aldığınız sınırlı entropi ile ilgilidir. Bazı tarayıcılar için entropi, hep birlikte sadece 41 bit kadar düşüktür. Math.random () işlevini birden çok kez çağırmak entropiyi yükseltmez. Gerçekten benzersiz v4 UUID'leri istiyorsanız, üretilen UUID başına en az 122 bit entropi üreten şifreleme açısından güçlü bir RNG kullanmanız gerekir.
mlehmk

36

Gerçekten de çarpışmalar var ama sadece Google Chrome altında. Konuyla ilgili deneyimime buradan göz atın

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

(2019 itibarıyla bağlantı kesildi. Arşiv bağlantısı: https://web.archive.org/web/20190121220947/http://devoluk.com/google-chrome-math-random-issue.html .)

Çarpışmalar yalnızca Math.random'un ilk birkaç çağrısında meydana geliyor gibi görünüyor. Çünkü yukarıdaki createGUID / testGUIDs yöntemini çalıştırırsanız (ki bu kesinlikle denediğim ilk şeydi), hiçbir şekilde çarpışma olmadan çalışır.

Bu yüzden tam bir test yapmak için Google Chrome'u yeniden başlatmanız, 32 bayt oluşturmanız, Chrome'u yeniden başlatmanız, oluşturmanız, yeniden başlatmanız, oluşturmanız gerekir ...


2
Bu oldukça endişe verici - bir hata raporu veren var mı?
UpTheCreek

1
Özellikle javascript'teki daha iyi rastgele sayı üreteçlerine bağlantı gibi: baagoe.com/en/RandomMusings/javascript
Leopd

maalesef, söz konusu bağlantı artık koptu :(
Gus


7
Bu hatanın giderilip giderilmediğini onaylayan var mı?
Xdrone

21

Sırf diğer insanların bunun farkında olması için - burada bahsedilen UUID oluşturma tekniğini kullanarak şaşırtıcı derecede çok sayıda görünür çarpışmaya rastlıyordum. Bu çarpışmalar, rastgele sayı üretecim için seedrandom'a geçtikten sonra bile devam etti . Tahmin edebileceğiniz gibi, bu beni saçlarımı koparttı.

Sonunda sorunun (neredeyse?) Yalnızca Google'ın web tarayıcı botlarıyla ilişkili olduğunu anladım. Kullanıcı aracısı alanında "googlebot" ile istekleri yok saymaya başladığım anda, çakışmalar ortadan kalktı. JS komut dosyalarının sonuçlarını yarı akıllı bir şekilde önbelleğe almaları gerektiğini tahmin ediyorum, sonuçta örümcek tarayıcılarının normal tarayıcıların yaptığı gibi davranması için güvenilemez.

Sadece bir Bilginize.


2
Metrik sistemimizle aynı sorunla karşılaştık. Tarayıcıda oturum kimlikleri oluşturmak için 'node-uuid' modülünü kullanan binlerce UUID çakışması görüyordu. Başından beri googlebot olduğu ortaya çıktı. Teşekkürler!
domkck

4

Bunu sorunuza yorum olarak göndermek istedim, ancak görünüşe göre StackOverflow buna izin vermiyor.

Chrome'da gönderdiğiniz UUID algoritmasını kullanarak 100.000 yinelemenin temel bir testini yaptım ve hiçbir çarpışma olmadı. İşte bir kod parçacığı:

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);

Burada başka bir şey olmadığına emin misin?


4
Evet, ben de bazı yerel testler yaptım ve hiçbir çarpışma olmadı. Farklı kullanıcıların makinelerinde üretilen UUID'ler arasında çarpışmalar meydana gelir. Farklı makinelerde bazı veriler oluşturmam ve çarpışmaları kontrol etmem gerekebilir.
Muxa

2
Ayrıca, çakışmaların 3-4 hafta arayla oluşturulan UUID'ler arasında olduğunu fark ettim.
Muxa

Çok tuhaf. Hangi platformda koşuyorsunuz?
user533676

1
V8'in Math.random () uygulamasında bu kadar basit bir kusur olması pek olası görünmüyor, ancak Chromium 11, bunun yerine denemek istiyorsanız, window.crypto.getRandomValues ​​API'sini kullanarak güçlü rastgele sayı oluşturma desteği ekledi. Bkz. Blog.chromium.org/2011/06/… .
user533676

Windows 7 ve Windows XP kombinasyonu üzerinde çalışıyor.
Muxa

3

Başlangıçta bu UUID çözümünü yayınlayan yanıt , 2017-06-28 tarihinde güncellendi:

Bir Chrome geliştiricilerinin iyi makale Chrome, Firefox ve Safari Math.random PRNG kalitesi devlet tartışırken. tl; dr - 2015'in sonlarından itibaren "oldukça iyi", ancak kriptografik kalite değil. Bu sorunu çözmek için, ES6, cryptoAPI ve biraz JS sihirbazını kullanan yukarıdaki çözümün güncellenmiş bir sürümünü burada bulabilirsiniz :

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());


0

Buradaki cevaplar "soruna neden olan şey" ile ilgilidir. (Chrome Math.random çekirdek sorunu) ancak "bundan nasıl kaçınabilirim?"

Hala bu sorunu nasıl önleyeceğinizi arıyorsanız, bu cevabı bir süre önce bu sorunun üstesinden gelmek için Broofa'nın işlevini değiştirilmiş bir yaklaşım olarak yazdım. İlk 13 onaltılık sayıyı zaman damgasının onaltılık bir bölümü ile kaydırarak çalışır, yani Math.random aynı tohumda olsa bile aynı milisaniyede oluşturulmadığı sürece farklı bir UUID oluşturacaktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.