Найти тему
Tominoff

Генерация одноразовых смс–кодов в Node.js

Относительно недавно я проводил ревизию зависимостей в нашем бэкенде на ноде и внимание моё привлекла одна маленькая библиотечка — generate-sms-verification-code.

Как понятно из названия, единственная её задача — генерировать цифровые смс коды для верификации.

Сама по себе, библиотека очень простая, исходный код помещается в 20 строчек, и она использует Math.random. Все популярные библиотеки, которые я полистал на npm, были построены именно на старом добром методе получения псевдослучайного числа.

В ноде, начиная с версии 12.19, во встроенном модуле crypto, имеется функция randomInt, которая позволяет получать случайное целое число в указанном диапазоне, и, для целей OTP (one time password) алгоритм, используемый в crypto, подходит гораздо лучше.

Отличия алгоритмов randomInt и Math.random
Я не спец в C++, но из того что я смог понять из исходников Node.js — реализация Math.random основана на алгоритме xorshift128+, в то время как, crypto.randomInt базируется на RAND_bytes из OpenSSL.
Алгоритм получения псевдослучайного числа в randomInt является криптостойким, в отличие от Math.random. Таким образом, предугадать следующее число будет чрезвычайно затруднительно.

Итак, генерация смс–кодов без зависимостей

В большинстве случаев, когда вам надо генерировать цифровые коды, будет вполне достаточно такой простой функции:

Элементарный генератор кодов
Элементарный генератор кодов

Генерация по алфавиту

Помимо таких простых случаев с цифровыми кодами, иногда может потребоваться генерировать что–то посерьёзнее, например, буквенно–цифровые коды, и тут мы сталкиваемся с необходимостью заводить словарь и выбирать случайные символы из этого словаря.

Это можно сделать точно так же, вручную, но зачем, когда для решения подобных проблем я уже разработал готовую библиотеку — node-verification-code

Основные отличия от других:

  1. Использует crypto.randomInt, а не Math.random
  2. Использует преаллоцированный буфер для размещения результатов
  3. Не имеет ограничений* по количеству символов для генерации последовательностей (*результат, очевидно, ограничен максимальным размером буфера)
  4. Позволяет использовать собственный алфавит, а не только цифровые последовательности

Библиотека не тащит за собой никаких зависимостей и предлагает чудовищно простой интерфейс для работы.

По факту имеется всего 3 функции:

  1. getDigitalCode – возвращает буфер с рандомным числовым кодом
  2. sequenceFromAlphabet – создаёт функцию–последовательность из указанного алфавита
  3. createGenerator – создаёт функцию для получения случайного значения из функции–последовательности

С getDigitalCode, думаю, всё понятно — передаём размерность и получаем результат в виде объекта Buffer.

sequenceFromAlphabet/createGenerator

Библиотека строится на двух основных понятиях — функция–генератор и функция–последовательность.

Функция–последовательность принимает количество символов, которые требуется составить в строку и возвращает результат.

Логика работы абсолютно такая же, как я демонстрировал выше в примере с generateOTPCode:

Да мы просто упаковали в функцию наш предыдущий вариант, требуемый charCount нам будет приходить из createGenerator
Да мы просто упаковали в функцию наш предыдущий вариант, требуемый charCount нам будет приходить из createGenerator

sequenceFromAlphabet создаёт такую же функцию, но вместо возврата рандомного числа, возвращает рандомный элемент из переданного алфавита.

createGenerator принимает в качестве аргумента функцию–последовательность и создаёт, собственно, сам генератор случайных данных.

Под капотом эта функция пре–аллоцирует в памяти буфер, разбивает свою работу на несколько итераций, на каждой из которых дополняет данными буфер, и в итоге возвращает усечённый буфер с результатом.

Git репозиторий:

GitHub - ikenfin/node-verification-code: Generate any-sized verification codes in Node.js.

Npm пакет:

node-verification-code

Полезные ссылки:

Криптостойкость алгоритма: https://ru.wikipedia.org/wiki/%D0%9A%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B0%D1%8F_%D1%81%D1%82%D0%BE%D0%B9%D0%BA%D0%BE%D1%81%D1%82%D1%8C

Разбор алгоритма xorshift+: https://hackernoon.com/how-does-javascripts-math-random-generate-random-numbers-ef0de6a20131

Взлом xorshift+: https://habr.com/ru/company/dcmiran/blog/584692/

Описание RAND_bytes в OpenSSL: https://www.openssl.org/docs/manmaster/man3/RAND_bytes.html

Git репозиторий с исходниками Node.js: https://github.com/nodejs/node