Чи справний ваш генератор випадкових чисел? Контрольний список практичного аудиту

Коли ви проводите розіграш, обираєте команди випадковим чином або призначаєте завдання за допомогою лотереї, справедливість не є обов’язковою — це вся суть. Але «випадкове» не означає автоматично «чесне». Приховані зміщення в генераторах випадкових чисел (ГВЧ) можуть спотворювати результати непомітними способами, якщо ви не знаєте, де шукати.
Ця стаття містить практичний контрольний список для перевірки будь-якого RNG на справедливість, незалежно від того, використовуєте ви наш Генератор випадкових чисел чи створюєте власний.
Що означає чесність для розіграшів, призначених для користувачів
Чесний RNG дає результати, де:
- Uniformity — кожен результат має однакову ймовірність (для рівномірного розподілу)
- Незалежність — на кожен розіграш не впливають попередні розіграші
- Непередбачуваність — жоден спостерігач не може передбачити наступний результат краще, ніж випадковість
- Можливість перевірки — учасники можуть підтвердити, що процес не маніпулював
Пропуск будь-якого з цих порушує довіру, навіть якщо результати «виглядають» випадковими.
Поширені джерела упередженості
Bad Seeds
A PRNG (генератор псевдовипадкових чисел) настільки ж непередбачуваний, як і його початкове значення. Поширені погані насіння:
- Поточна мітка часу — передбачувана з точністю до мілісекунди; зловмисник, який приблизно знає, коли відбувається розіграш, може відтворити результат
- Послідовні лічильники — зовсім не випадкові
- Введення користувача — учасники можуть маніпулювати
Завжди завантажуйте з криптографічного джерела: crypto.getRandomValues() у браузерах, /dev/urandom в Linux або crypto.randomBytes() у Node.js.
Modulo Bias
Тонка та поширена помилка: використання оператора modulo для зменшення випадкового числа до діапазону.
// BIASED — якщо діапазон RNG не кратний 6,
// деякі результати є трохи більш імовірними
const roll = randomUint32() % 6; // ПРАВИЛЬНО — вибірка відхилення
функція fairDiceRoll() { const max = Math.floor(0xFFFFFFFF / 6) * 6; нехай значення; do { value = crypto.getRandomValues(new Uint32Array(1))[0]; } while (значення >= max); повернення значення % 6;
Для 6-сторонньої матриці зміщення за модулем із 32-бітним цілим числом становить лише ~0,00000009%. Але для більших діапазонів або 8-бітних значень це стає значущим.
Приховані фільтри
Деякі системи розіграшу мовчки виключають певні результати (наприклад, відфільтровують «останніх переможців» або повторюють результати, які не подобаються оператору). Це порушує справедливість, навіть якщо основний RNG ідеальний. Задокументуйте та розкрийте всі правила фільтрації перед розіграшем.
Контрольний список аудиту для операторів
- Джерело ентропії — RNG завантажується з криптографічного джерела? (Не Math.random, не мітки часу)
- Тест однорідності — Виконайте понад 10 000 зразків і застосуйте тест хі-квадрат. p-value має бути > 0,05
- Modulo bias — чи використовує код вибірку відхилень чи метод неупередженого зіставлення?
- Independence — Чи пов’язані послідовні розіграші? Виконайте тест автокореляції на великих вибірках
- Перегляд коду — Чи код розіграшу є відкритим кодом чи доступним для перевірки? Прихований код може містити бекдори
- Відкриття фільтрації — Чи відфільтровано, повторно згорнуто чи виключено будь-які результати? Це має бути розкрито
- Timing — Чи може оператор переглянути результати перед публікацією? Якщо так, вони можуть вибірково відкидати несприятливі розіграші
Шаблон прозорості для публічних розіграшів
Для розіграшів із високими ставками (призи, завдання, вибір) використовуйте схему фіксації-розкриття:
- Перед розіграшем: Згенеруйте випадкове початкове число. Опублікуйте його хеш SHA-256 («зобов’язання») — наприклад, у соціальних мережах або документі з міткою часу
- Запустіть розіграш: Використовуйте початковий код, щоб створити результати за допомогою детермінованого алгоритму
- Після жеребкування: Опублікуйте початковий код. Будь-хто може підтвердити, що:
- Початковий код створює опублікований хеш
- Початковий код + алгоритм дає оголошені результати
Використовуйте наш Генератор хешів, щоб створити та перевірити хеш зобов’язань.
// Фаза зобов’язань
const seed = crypto.randomBytes(32).toString('hex');
const commitment = sha256(seed); // опублікувати це перед розіграшем // Етап малювання
const result = deterministicDraw(seed, учасників); // Фаза розкриття
// опублікувати вихідний код — будь-хто може перевірити sha256(seed) === commitmentПовідомлення користувачів про чесність
Довіра вимагає прозорості. Під час публічних розіграшів:
- Вкажіть використаний метод RNG (наприклад, «Web Crypto API»)
- Опублікувати алгоритм малювання (навіть псевдокод допомагає)
- Використовуйте commit-reveal для перевірки
- Записуйте та публікуйте журнали тиражів із мітками часу
- Дозволити незалежним спостерігачам перевіряти результати
FAQ
Чи достатньо Math.random?
№ Math.random() використовує генератор псевдовипадкових чисел (PRNG), який не є криптографічно захищеним. Його вихід можна передбачити, якщо відомий внутрішній стан. Для чесних розіграшів використовуйте crypto.getRandomValues() у браузері або crypto.randomInt() у Node.js.
Як я можу довести, що розіграш не був підроблений?
Використовуйте схему виявлення фіксації: перед розіграшем опублікуйте хеш випадкового початкового числа (зобов’язання). Після розіграшу розкрийте насіння. Будь-хто може переконатися, що вихідний код створює опублікований хеш і оголошені результати.
Скільки зразків мені потрібно для базової перевірки упередженості?
Для простої перевірки однорідності за N результатами вам потрібно принаймні 100 × N зразків (наприклад, 1000 зразків для жеребкування з 10 варіантами). Застосуйте тест хі-квадрат: якщо p-значення вище 0,05, розподіл достатньо рівномірний. Для серйозних перевірок використовуйте понад 10 000 зразків.
Пов’язані інструменти та статті
- Генератор випадкових чисел — криптографічно справедливі випадкові числа
- Генератор хешів — створюйте хеші зобов’язань для розіграшів, які можна перевірити
- Навіщо використовувати генератор PIN-кодів? — безпечні випадкові PIN-коди
- Цифровий контрольний список гігієни — загальні методи безпеки