Захистити випадкові числа в JavaScript?


80

Як створити криптографічно захищені випадкові числа в javascript?


3
Що саме ви маєте на увазі під "криптографічно"? Використовуйте Math.random (), щоб повернути випадкове число від 0 до 1. Його технічно псевдовипадкове число, оскільки насправді не існує жодного (простого) способу генерування справжніх випадкових чисел.
— художник логіки

26
Я думаю, що питання стосується генератора випадкових чисел, який підходить для криптографії. Напр. Модульний RND, реалізований за замовчуванням багатьма мовами, не підходить.
— winwaed


18
@Logic Artist - Ні, Math.random не є криптографічно безпечним. Криптографічно безпечний стандартний термін, який означає, що цінність непередбачувана, навіть для супротивника, який готовий вкласти значну кількість часу та енергії, намагаючись передбачити її або відрізнити від випадкової.
— DW

Також див.
— Невпевнена

Відповіді:


24

Ви можете, наприклад, використовувати рух миші як насіння для випадкових чисел, зчитувати час і положення миші, коли трапляється подія onmousemove, подавати ці дані до функції відбілювання, і у вас буде під рукою кілька випадкових випадків першого класу. Хоча переконайтеся, що користувач досить перемістив мишу перед тим, як використовувати дані.

Редагувати: Я сам трохи погрався з концепцією, створивши генератор паролів, я б не гарантував, що моя функція відбілювання бездоганна, але, постійно пересіваючись, я майже впевнений, що цього достатньо для роботи: ebusiness.hopto.org /generator.htm

Edit2: Зараз це начебто працює зі смартфонами, але лише відключаючи функцію дотику, поки ентропія зібрана. Інший спосіб Android не працюватиме належним чином.


11
Ось криптобібліотека з ліцензією BSD та генератором випадкових чисел: crypto.stanford.edu/sjcl
— aaaaaaaaaaaa

Здається, це відповідає вимогам ОП.
— Президент Джеймс К. Полк,

SJCL (Стенфордська криптотека) виглядає чудовим вибором. У них є опублікована стаття, де докладно описується, як вони генерують криптографічно випадкові числа, і їх підхід виглядає твердим і продуманим.
— DW

У мене є пропозиція eBusiness: Додайте поле роздільника, яке призведе до того, що цей рядок буде встановлено між кожним .password spanтегом, щоб полегшити копіювання / вставлення / маніпулювання. Наприклад, в даний час, якщо я скопіюю та вставлю згенеровані рядки, вони будуть вставлені як один довгий рядок.
— trusktr

Зверніть увагу, що це не працює на мобільній платформі, оскільки немає миші, тому тим, хто використовує цю функцію, знадобиться резервне джерело випадкових чисел.
— Метт Ескрідж,

61

У WHATWG обговорювали питання щодо додавання цього до об’єкта window.crypto. Ви можете прочитати обговорення та перевірити запропонований API та помилку webkit (22049).

Щойно перевірив наступний код у Chrome, щоб отримати випадковий байт:

(function(){
  var buf = new Uint8Array(1);
  window.crypto.getRandomValues(buf);
  alert(buf[0]);
})();


Це працює в IE 11, якщо замінити window.cryptoна window.msCrypto.
— Michael Kropat

28

Для того, я думаю, що ваші найкращі ставки:

  1. window.crypto.getRandomValues ​​або window.msCrypto.getRandomValues
  2. Функція довільних слів бібліотеки sjcl ( http://crypto.stanford.edu/sjcl/ )
  3. Генератор випадкових чисел бібліотеки isaac (який заснований Math.random, тому насправді не криптографічно захищений) ( https://github.com/rubycon/isaac.js )

window.crypto.getRandomValues ​​вже деякий час впроваджується в Chrome, а відносно недавно і у Firefox. На жаль, Internet Explorer 10 і раніше не реалізують цю функцію. IE 11 має window.msCrypto, який виконує те саме. sjcl має великий генератор випадкових чисел, породжений рухами миші, але завжди є ймовірність, що або миша не переміститься в достатній мірі, щоб заробити генератор, або що користувач перебуває на мобільному пристрої, де рух миші не відбувається. Таким чином, я рекомендую мати резервний випадок, коли ви все одно можете отримати небезпечне випадкове число, якщо немає вибору. Ось як я впорався з цим:

function GetRandomWords (wordCount) {
    var randomWords;

    // First we're going to try to use a built-in CSPRNG
    if (window.crypto && window.crypto.getRandomValues) {
        randomWords = new Int32Array(wordCount);
        window.crypto.getRandomValues(randomWords);
    }
    // Because of course IE calls it msCrypto instead of being standard
    else if (window.msCrypto && window.msCrypto.getRandomValues) {
        randomWords = new Int32Array(wordCount);
        window.msCrypto.getRandomValues(randomWords);
    }
    // So, no built-in functionality - bummer. If the user has wiggled the mouse enough,
    // sjcl might help us out here
    else if (sjcl.random.isReady()) {
        randomWords = sjcl.random.randomWords(wordCount);
    }
    // Last resort - we'll use isaac.js to get a random number. It's seeded from Math.random(),
    // so this isn't ideal, but it'll still greatly increase the space of guesses a hacker would
    // have to make to crack the password.
    else {
        randomWords = [];
        for (var i = 0; i < wordCount; i++) {
            randomWords.push(isaac.rand());
        }
    }

    return randomWords;
};

Вам потрібно буде включити sjcl.js та isaac.js для цієї реалізації, і не забудьте запустити колектор ентропії sjcl відразу після завантаження вашої сторінки:

sjcl.random.startCollectors();

sjcl має подвійну ліцензію BSD і GPL, тоді як isaac.js - це MIT, тому абсолютно безпечно використовувати будь-який із них у будь-якому проекті. Як зазначалося в іншій відповіді, clipperz - це ще один варіант, однак з якоїсь химерної причини він ліцензований згідно AGPL. Я ще не бачив нікого, хто, здається, розуміє, які наслідки це має для бібліотеки JavaScript, але я б уникнув цього.

Одним із способів поліпшення коду, який я опублікував, може бути збереження стану генератора випадкових чисел isaac у localStorage, тому він не пересилається кожного разу, коли сторінка завантажується. Ісаак генерує випадкову послідовність, але для цілей криптографії насіння є дуже важливим. Посів за допомогою Math.random - це погано, але принаймні трохи менше, якщо це не обов’язково при кожному завантаженні сторінки.


Я дотримувався цього підходу на github.com/simbo1905/srp-6a-demo/blob/master/srp/Client/lib/…, щоб створити випадкове 128-шістнадцяткове число. Це користувачі window.crypto else isaac. Якщо доведеться використовувати isaac, він буде розігрівати генератор при завантаженні, пропускаючи випадкові випадки протягом 0,1 с. Поле введення тексту onkeyup in також random16byteHex.advance(Math.floor(event.keyCode/4));надалі пропускає випадкові числа вперед на кілька мілісекунд. Це зробило б випадкові випадки використання iaac, що використовуються в цій програмі браузера, залежати від вводу користувача та швидкості обладнання / браузера, так що важко вгадати.
— simbo1905

1
@ZeroG Щодо Вашого коментаря щодо SJCL: "завжди є ймовірність, що або миша не просунеться в достатній мірі, щоб заробити генератор, або що користувач перебуває на мобільному пристрої, де рух миші не відбувається" . Зараз він добре працює на мобільних пристроях, оскільки ентропія тепер збирається з touchmove( тягнути # 151 ) та devicemotion( тягнути # 79 ).
— TachyonVortex

1
схоже, sjcl вже використовує window.crypto є доступним
— Алесь

14

Використовуйте window.crypto.getRandomValuesтак:

var random_num = new Uint8Array(2048 / 8); // 2048 = number length in bits
window.crypto.getRandomValues(random_num);

Це підтримується у всіх сучасних браузерах і використовує генератор випадкових випадків операційної системи (наприклад /dev/urandom). Якщо вам потрібна сумісність з IE11, вам доведеться використовувати їх префіксну реалізацію через var crypto = window.crypto || window.msCrypto; crypto.getRandomValues(..).

Зверніть увагу, що window.cryptoAPI також може генерувати ключі прямо , що може бути кращим варіантом.


Думаю, ви мали на увазі Uint8Array (перевірте орфограму)
— Flyingkiwi

1
Чи правильний термін тут "довжина ключа"? І чи не довжини ключів виражаються в бітах?
— Інодінг

1
Як використовувати window.crypto.getRandomValues, якщо я хочу сформувати випадкові числа в певному діапазоні, скажімо 4000-64000, і мені потрібно кожен раз 1 випадкове число.
— Сід

2
@Sid Це звучить як чудове запитання. Запитайте !
— phihag

2
@phihag: вже робив, відповідей поки що немає. stackoverflow.com/questions/41437492/…
— Сід,


4

Можливо, ви захочете спробувати http://sourceforge.net/projects/clipperzlib/ У ньому є реалізація Fortuna, яка є криптографічно захищеним генератором випадкових чисел. (Погляньте на src / js / Clipperz / Crypto / PRNG.js). Здається, миша також використовується як джерело випадковості.


Більш детальна інформація про бібліотеку доступна тут clipperz.com/open_source/javascript_crypto_library
— емір

1
Гарна відповідь, на жаль, вона ліцензована згідно AGPL, що, на мою думку, не сумісне з моїм проектом.
— Кайл

Станом на 2 травня 2014 р. Кліпперц перейшов з AGPL на BSD за допомогою коміту c9f12e87c7ac88e4612de4d1d70df7c53f77e2ad
— GGG,

1

Перш за все, вам потрібно джерело ентропії. Наприклад, рух миші, пароля або будь-якого іншого. Але всі ці джерела дуже далекі від випадкових і гарантують вам 20 біт ентропії, рідко більше. Наступним кроком, який вам потрібно зробити, є використання такого механізму, як "KDF на основі пароля", що зробить обчислювально важким відрізнити дані від випадкових.


0

Багато років тому вам довелося застосувати власний генератор випадкових чисел і засіяти його ентропією, зібраною за допомогою руху миші та інформації про час. Це була ера Флогістона з криптографії JavaScript. Цими днями нам доводиться window.cryptoпрацювати.

Якщо вам потрібно випадкове ціле число , відмінним вибором є випадкове число-csprng . Він надійно генерує серію випадкових байтів, а потім перетворює її в неупереджене випадкове ціле число.

const randomInt = require("random-number-csprng");
(async function() {
    let random = randomInt(10, 30);
    console.log(`Your random number: ${random}`);
})();

Якщо вам потрібно випадкове число з плаваючою комою, вам доведеться виконати трохи більше роботи. Як правило, проте, захищена випадковість - це ціла проблема, а не проблема з плаваючою комою.

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