JavaScriptでUUIDを生成するときの衝突?


94

これはこの質問に関連していますこの回答の以下のコードを使用して、JavaScriptでUUIDを生成しています。

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

このソリューションは正常に機能しているようですが、衝突が発生しています。ここに私が持っているものがあります:

  • Google Chromeで実行されるWebアプリ。
  • 16ユーザー。
  • これらのユーザーによって、過去2か月間に約4000のUUIDが生成されています。
  • 約20回の衝突が発生しました。たとえば、今日生成された新しいUUIDは約2か月前と同じでした(別のユーザー)。

この問題の原因は何ですか?どうすれば回避できますか?


2
適切な乱数を現在の時間(ミリ秒単位)と組み合わせます。まったく同時に衝突する乱数のオッズは、本当に、本当に、本当に低いです。
jfriend00

7
@ jfriend00それを行う必要がある場合、それは「適切な乱数」ではなく、まともな疑似乱数でもありません。
Attila O.

2
その(r&0x3|0x8)部分は何を意味していますか/評価は?
クリスティアン2014

Date.now()。toString()をそれに追加するのはどうですか?
Vitim.us

4
UUIDとは無関係に、アーキテクチャに大きな問題があります-クライアントが意図的に衝突するIDを生成する可能性があります。信頼できるシステムでのみIDを生成します。ただし、回避策として、クライアントが生成したIDの前にuser_idを付加して、敵対/障害のあるクライアントが自分自身としか衝突できない(そしてサーバー側でそれを処理できる)ようにします。
Dzmitry Lazerka 16

回答:


35

私の推測では、Math.random()何らかの理由でシステムで問題が発生しています(奇妙に聞こえる)。これは、衝突を起こした人について私が見た最初のレポートです。

node-uuidには、そのコード内の16進数の分布をテストするために使用できるテストハーネスがあります。それが問題ないように見える場合はMath.random()、そうではありません。そこで、使用しているUUID実装をuuid()そこでメソッドに置き換えて、まだ良い結果が得られるかどうかを確認してください。

[更新:起動時のバグに関するVeselinのレポートを見たところMath.random()です。問題は起動時にのみ発生するため、node-uuidテストが役立つ可能性はほとんどありません。devoluk.comリンクで詳しく説明します。]


1
ありがとうございます。uuid.jsはブラウザの強力な暗号化を使用するので、これを使用します。衝突がないか確認します。
Muxa、2011

あなたが参照しているuuid.jsコードへのリンクを提供できますか?(申し訳ありませんが、どのlibを意味するのか
わかり

10
これまでに衝突はありませんでした:)
Muxa

とにかく、それがChromeであり、起動時にのみ、アプリは上記の関数を使用して、たとえば10個のGUIDの行を生成および破棄できます:)
Vinko Vrsalovic

問題は、Math.random()から得られる限られたエントロピーにあります。一部のブラウザでは、エントロピーが合計で41ビットと低い場合があります。Math.random()を複数回呼び出しても、エントロピーは発生しません。本当に一意のv4 UUIDが必要な場合は、生成されたUUIDごとに少なくとも122ビットのエントロピーを生成する暗号的に強力なRNGを使用する必要があります。
mlehmk '25

36

実際、衝突はありますが、Google Chromeでのみです。このトピックに関する私の経験をここで確認してください

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

(2019年の時点でリンクは壊れています。アーカイブリンク:https : //web.archive.org/web/20190121220947/http ://devoluk.com/google-chrome-math-random-issue.html )

衝突はMath.randomの最初の数回の呼​​び出しでのみ発生するようです。上記のcreateGUID / testGUIDsメソッド(明らかに私が最初に試みたもの)を実行した場合、それはまったく衝突なしで機能します。

したがって、完全なテストを行うには、Google Chromeを再起動し、32バイトを生成し、Chromeを再起動し、生成、再起動、生成する必要があります...


2
それはかなり心配です-誰かがバグレポートを上げましたか?
UpTheCreek

1
特にjavascriptのより良い乱数ジェネレータへのリンクのように:baagoe.com/en/RandomMusings/javascript
Leopd

悲しいことに、リンクが壊れています:(
Gus


7
このバグが解決されたかどうか誰でも確認できますか?
Xdrone、2014

20

他の人々がこれを認識できるようにするために-ここで言及したUUID生成技術を使用して、驚くほど多数の見かけ上の衝突が発生していました。乱数ジェネレーターをシードランダムに切り替えた後も、これらの衝突は続きました。ご想像のとおり、髪を引き裂いてしまいました。

私は最終的に、問題が(ほとんど?)GoogleのWebクローラーボットにのみ関連していることがわかりました。ユーザーエージェントフィールドに「googlebot」を含むリクエストを無視し始めるとすぐに、衝突はなくなりました。私は彼らがJSスクリプトの結果をある程度インテリジェントな方法でキャッシュする必要があると思います。その結果、スパイダーブラウザーは通常のブラウザーのように動作することを期待できません。

参考までに。


2
メトリックシステムで同じ問題に遭遇しました。ブラウザでセッションIDを生成するために「node-uuid」モジュールを使用して、何千ものUUIDの衝突を確認していました。ずっとgooglebotだったことがわかりました。ありがとう!
domkck 2017年

4

これをあなたの質問へのコメントとして投稿したかったのですが、どうやらStackOverflowは私を許可しません。

投稿したUUIDアルゴリズムを使用して、Chromeで100,000回の基本的なテストを実行したところ、衝突はありませんでした。ここにコードスニペットがあります:

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

ここで他に何も起こっていないのは確かですか?


4
はい、私はいくつかのローカルテストも実行し、衝突はありませんでした。異なるユーザーのマシンで生成されたUUID間で衝突が発生します。別のマシンでデータを生成し、衝突をチェックする必要があるかもしれません。
Muxa

2
また、3週間から4週間離れて生成されたUUID間で競合が発生していることにも気付きました。
Muxa、2011

非常に奇妙な。どのプラットフォームで実行していますか?
user533676 2011

1
V8のMath.random()にそれほど基本的な欠陥があるとは思えないが、代わりに試してみたい場合は、Chromium 11がwindow.crypto.getRandomValues APIを使用した強力な乱数生成のサポートを追加した。blog.chromium.org/2011/06/…を参照してください。
user533676 2011

Windows 7とWindows XPを組み合わせて実行します。
Muxa

3

このUUIDソリューションを最初に投稿した回答は、2017-06-28に更新されました。

Chrome、Firefox、SafariでのMath.random PRNGの品質の状態について議論しているChrome開発者から優れた記事。tl; dr-2015年後半の時点では「かなり良い」ですが、暗号品質ではありません。この問題に対処するために、ES6、cryptoAPI、および私が信用できないちょっとしたJSウィザードを使用する上記のソリューションの更新バージョンを次に示します。

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

ここでの答えは「問題の原因は何ですか?」(Chrome Math.randomシードの問題)ではありませんが、「どうすれば回避できますか?」

この問題を回避する方法をまだ探している場合は、この答えを、この正確な問題を回避するためのBroofaの機能の修正版としてしばらく書いた。これは、最初の13桁の16進数をタイムスタンプの16進数部分でオフセットすることで機能します。つまり、Math.randomが同じシード上にある場合でも、まったく同じミリ秒で生成されない限り、異なるUUIDを生成します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.