Зіткнення під час генерації UUID в JavaScript?


94

Це стосується цього питання . Я використовую наведений нижче код з цієї відповіді для створення UUID в 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);
});

Це рішення, здається, працює нормально, але я отримую колізії. Ось що я маю:

  • Веб-додаток, що працює в Google Chrome.
  • 16 користувачів.
  • за останні 2 місяці ці користувачі створили близько 4000 UUID.
  • У мене було близько 20 зіткнень - наприклад, новий UUID, що генерується сьогодні, був таким же, як і близько 2 місяців тому (інший користувач).

Що викликає це питання і як я можу його уникнути?


2
Поєднайте хороше випадкове число з поточним часом (у мілісекундах). Шанси випадкового числа, що стикаються точно в один і той же час, дійсно, дійсно, дуже низькі.
jfriend00

7
@ jfriend00, якщо вам потрібно це зробити, то це не "хороше випадкове число", навіть не гідне псевдовипадкове число.
Аттіла О.

2
що означає (r&0x3|0x8)порція / оцінка?
Крістіан

Як щодо додавання до нього Date.now (). ToString ()?
Vitim.us

4
У вашій архітектурі є велика проблема, не пов'язана з UUID - клієнт може навмисно генерувати зіштовхуються ідентифікатори. Створюйте ідентифікатори лише в довіреній системі. Однак як вирішення, передбачте створені клієнтом ідентифікатори з user_id, так що противник / несправний клієнт може зіткнутися лише з самим собою (і обробляти це на стороні сервера).
Дмитро Лазерка

Відповіді:


35

Я найкраще здогадуюсь, що Math.random()з вашої системи чомусь зламано (химерно, як це звучить). Це перший звіт, який я бачив, як хтось зіткнувся.

node-uuidмає тестовий джгут, який можна використовувати для перевірки розподілу шістнадцяткових цифр у цьому коді. Якщо це виглядає нормально, то це не Math.random()так, то спробуйте замінити реалізацію UUID, яку ви використовуєте, на uuid()метод там і перевірте, чи отримаєте ви хороші результати.

[Оновлення: Щойно побачив звіт Веселіна про помилку Math.random()при запуску. Оскільки проблема полягає лише в запуску, node-uuidтест навряд чи буде корисним. Я детальніше прокоментую посилання на devoluk.com.]


1
Дякую, зараз я переходжу з uuid.js, оскільки він використовує сильну криптовалюту браузера, якщо вона доступна. Побачимо, чи є зіткнення.
Muxa

чи можете ви надати посилання на код uuid.js, на який ви посилаєтесь? (вибачте, не впевнений, про яку
ліб

10
Досі не було зіткнень :)
Muxa

У будь-якому випадку, якщо це Chrome і лише при запуску, ваш додаток може генерувати та відкидати ряд, скажімо, десяти посібників за допомогою вищевказаної функції :)
Vinko Vrsalovic

Проблема полягає в обмеженій ентропії, яку ви отримуєте від Math.random (). Для деяких браузерів ентропія становить всього 41 біт разом. Виклик Math.random () кілька разів не підвищить ентропію. Якщо ви дійсно хочете унікальних U4 UUID, вам потрібно використовувати криптографічно сильну RNG, яка виробляє щонайменше 122-бітну ентропію на один створений UUID.
mlehmk

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

20

Просто для того, щоб інші люди могли знати про це - я зіткнувся з дивовижно великою кількістю очевидних зіткнень за допомогою згаданої тут техніки покоління UUID. Ці зіткнення тривали навіть після того, як я перейшов на seedrandom для мого генератора випадкових чисел. Це змусило мене виривати волосся, як ви можете собі уявити.

Зрештою я зрозумів, що проблема (майже?) Була пов'язана виключно з веб-ботами веб-сканерів Google. Як тільки я почав ігнорувати запити з "googlebot" у полі користувача-агента, зіткнення зникли. Я здогадуюсь, що вони повинні кешувати результати сценаріїв JS якимось напівінтелектуальним способом, з кінцевим результатом того, що їх павукоподібний браузер не може розраховувати на те, що він буде вести себе так, як це роблять звичайні браузери.

Просто вигадка.


2
Потрапили в те саме питання з нашою системою показників. Бачив тисячі зіткнень UUID за допомогою модуля 'node-uuid' для створення ідентифікаторів сеансу в браузері. Виявляється, це був googlebot весь час. Дякую!
domkck

4

Я хотів опублікувати це як коментар до вашого питання, але, очевидно, StackOverflow не дозволив мені.

Я щойно провів рудиментарний тест 100 000 ітерацій у Chrome за допомогою алгоритму UUID, який ви опублікували, і не зіткнувся. Ось фрагмент коду:

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, що генеруються на машинах різних користувачів. Можливо, мені доведеться генерувати деякі дані на різних машинах і перевіряти на колізії.
Мукса

2
Крім того, я помітив, що зіткнення між UUID, що породжуються 3-4 тижні.
Мукса

Дуже дивно. На якій платформі ти працюєш?
user533676

1
Здається, малоймовірний такий недолік у Math.random () V8, але Chromium 11 додав підтримку сильного генерації випадкових чисел за допомогою API window.crypto.getRandomValues, якщо ви хочете спробувати його. Дивіться blog.chromium.org/2011/06/… .
user533676

Працює на комбінації Windows 7 та Windows XP.
Muxa

3

Відповідь, яка спочатку розмістила це рішення UUID, оновлена ​​на 2017-06-28:

Гарна стаття від розробників Chrome обговорюють стан якості Math.random ПСЧ в Chrome, Firefox і Safari. tl; dr - Станом на кінець 2015 року це "досить добре", але не криптографічна якість. Щоб вирішити цю проблему, ось оновлена ​​версія вищезазначеного рішення, що використовує ES6, cryptoAPI та трохи JS Wizardy, за які я не можу брати кредит :

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 шістнадцяткових чисел на шістнадцяткову частину часової позначки, що означає, що навіть якщо Math.random знаходиться на одному і тому ж насінні, він все одно генерує інший UUID, якщо він не буде генерований з точно такою ж мілісекундою.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.