Захистити випадкові числа в 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.