Генератор токенов

Создаёт секреты через криптографический генератор браузера — hex, base64url или свой алфавит, плюс парольные фразы EFF — с энтропией в битах.

Создаётся…
Энтропия192.0 бит

Среднее время поиска при трёх скоростях перебора. Разброс между ними больше любой разницы, которую вносит сам секрет.

  • Против входа с ограничением частоты — 10 попыток/с10^49 лет
  • Против утёкшего медленного хеша (bcrypt, argon2) — 10⁴/с10^46 лет
  • Против утёкшего быстрого хеша (SHA-256, MD5) — 10¹²/с10^38 лет

Выбор из 64 символов, поэтому каждый несёт 6.00 бита.

Что делает этот инструмент

Он создаёт секреты — значения, вся защита которых в том, что их нельзя угадать. Выберите алфавит и длину для ключа API или пароля к базе данных либо перейдите к парольной фразе, вытянутой из опубликованного словаря, если набирать её придётся человеку. Каждый результат создаётся в вашем браузере и показывается с тем единственным числом, которое честно описывает его стойкость.

Это другая работа, нежели у генератора UUID на этом сайте. UUID должен быть уникальным, чтобы две записи никогда не столкнулись; секрет должен быть неугадываемым — требование более сильное и отказ иного рода. Предсказуемый идентификатор безвреден. Предсказуемый секрет — открытая дверь.

Откуда берётся случайность

Всё здесь черпается из crypto.getRandomValues, криптографически стойкого генератора случайных чисел браузера, засеваемого из пула энтропии операционной системы — того же источника, которым пользуются openssl rand и /dev/urandom.

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

Резонно спросить, вообще ли веб-страница — подходящее место для боевого секрета. Само порождение надёжно: это CSPRNG платформы, страница статическая, ничего не передаётся. Думать стоит о том, что вокруг: секрет, прошедший через буфер обмена, могут прочесть другие приложения, а вставленный в терминал обычно оседает в истории оболочки. Это те же соображения, что и при любом другом способе.

Смещение, спрятанное в очевидной реализации

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

При 62 буквенно-цифровых символах деление не нацело: 62 укладывается в 256 четыре раза, и остаётся 8. Эти 8 оставшихся значений байта — с 248 по 255 — сворачиваются на первые 8 символов алфавита, так что каждый из них получает пять шансов из 256, а остальные — четыре. Первые восемь символов оказываются примерно на 25 % вероятнее прочих.

Лекарство — выборка с отбрасыванием: отбросить тягу, попавшую в остаточный хвост, и тянуть заново, а не сворачивать её внутрь. Для алфавита в 62 символа это стоит около одной лишней тяги из тридцати двух, а для hex и base64url не стоит ничего: их размеры — степени двойки, и хвоста у них нет. Этот инструмент отбрасывает; в отдельно взятом токене эффекта не видно, и именно поэтому о нём стоит сказать.

Энтропия — единственная честная мера

Энтропия в битах говорит, насколько велико пространство равновероятных секретов: n бит означают 2^n возможностей. Она аддитивна и удобна для сравнения и ничего не говорит о том, как быстро кто-то способен это пространство перебрать, — и это достоинство, потому что та часть зависит от вещей, которых генератор знать не может.

Арифметика намеренно проста. Каждый символ вносит log2(размер алфавита) бит, а каждое слово — log2(размер словаря), поэтому:

  • Шестнадцатеричный символ — 4 бита, поэтому 32-символьный hex-токен ровно 128 бит: выписанный ключ AES-128.
  • Символ base64url — 6 бит, поэтому 22 символа перешагивают 128 бит, а 43 перешагивают 256.
  • Буквенно-цифровой символ — около 5,95 бита; исключение похожих опускает алфавит до 58, а символ до 5,86, что стоит примерно одного символа длины на каждые двенадцать.
  • Слово из длинного списка EFF — 12,925 бита, ведь в списке 7776 записей, то есть 6^5: пять бросков кости.
  • Слово из короткого списка — 10,34 бита, из 1296 записей, то есть 6^4.

Обычный ориентир — 128 бит, та точка, где полный перебор перестаёт быть стратегией, а не просто становится дорогим. Это 32 шестнадцатеричных символа, 22 символа base64url или 10 слов из длинного списка EFF.

Почему нет единого «времени взлома»

У секрета нет времени взлома. Оно есть у пары из секрета и того, что его стережёт, и страж весит куда больше самого секрета. То самое значение, поиск которого через форму входа занял бы больше, чем существует Вселенная, падает за один вечер, если оно хранилось как несолёный SHA-256, а база утекла.

Поэтому показаны три скорости вместо одного числа, и каждая названа:

  • Десять попыток в секунду — против входа с ограничением частоты. Это реальный потолок для того, кому приходится идти через вашу службу.
  • Десять тысяч попыток в секунду — против утёкшего парольного хеша, нарочно сделанного медленным: bcrypt с современной стоимостью или argon2. Эта медлительность и есть весь смысл таких функций.
  • Триллион попыток в секунду — против утёкшего хеша, никогда для паролей не предназначавшегося. SHA-256 и MD5 спроектированы быстрыми, а стойка из видеокарт быстра чрезвычайно.

Скорости округлены до степеней десяти намеренно. Что-либо точнее намекало бы на измерение одного конкретного противника, тогда как полезны здесь одиннадцать порядков между первой строкой и последней. Если секрету удобно во всех трёх, вопрос закрыт и без точных цифр.

Каждая величина — среднее, то есть половина пространства, а не всё оно: перебор в среднем находит ответ на полпути. А за пределом миллиона лет ответ даётся как порядок величины, потому что «четыреста триллионов лет» — не та длительность, которую кто-нибудь способен сопоставить. Вселенной около 10^10 лет, и это полезный ориентир для строк, уходящих так далеко.

Парольные фразы и откуда на самом деле их стойкость

Парольная фраза стойка ровно по одной причине: несколько слов выбраны наугад из большого списка. Не потому, что она длинная, и не потому, что похожа на язык. Шесть случайных слов из списка в 7776 — около 77 бит; шесть слов, придуманных человеком, стоят куда меньше, ведь люди выбирают неравномерно, а нападающий знает о расхожих оборотах ровно то же, что и вы.

Оба словаря здесь взяты у EFF — это опубликованные списки для diceware, загруженные без изменений. Это не собранные здесь списки, и это важно: у придуманного кем-то словаря неизвестен размер, неизвестно пересечение с другими списками и нет способа проверить приписанную ему энтропию.

  • В длинном списке 7776 слов — по одному на каждый бросок пяти костей — и он даёт 12,925 бита на слово.
  • В коротком 1296 слов — по одному на бросок четырёх костей — и он даёт 10,34 бита на слово. Слова в нём короче, ни одно не длиннее пяти букв, что облегчает набор ценой большего числа слов при той же стойкости.

Повторившееся слово в парольной фразе не изъян и здесь не перетягивается. Каждая тяга независима, так что любая конкретная пара слов ровно так же вероятна, как любая другая; отказ от повторов сузил бы пространство возможных фраз и сделал бы их чуть слабее, а не сильнее.

В обоих списках EFF есть горстка записей с дефисом: t-shirt, yo-yo, drop-down, felt-tip. Если разделитель тоже дефис, фразу с таким словом уже нельзя однозначно разобрать обратно на слова. Это беда отображения, а не безопасности, и пробел или точка в роли разделителя снимают её полностью.

Настройки, стоящие энтропии, и одна, которая не стоит

Исключение похожих символов меняет стойкость на читаемость, и обмен виден. Убрать 0, O, I и l — значит опустить буквенно-цифровой алфавит с 62 символов до 58, а это ровно тот алфавит base58, которым пользуется Bitcoin, и по той же причине. Каждый символ падает с 5,95 до 5,86 бита, так что токену нужен примерно один лишний символ на каждые двенадцать, чтобы остаться столь же стойким. Для всего, что человек прочтёт с экрана и наберёт в другом месте, это обычно того стоит.

Настройка предлагается лишь там, где что-то значит. Hex и base64url — это кодировки, а не наборы символов: их алфавиты закреплены спецификацией, и hex без 0 — уже не hex, декодировать его будет нечем.

Заглавная буква в каждом слове парольной фразы не добавляет ровно ничего, и показатель стойкости здесь намеренно не сдвигается при включении. Это одно и то же преобразование каждый раз, оно не создаёт ни единой новой возможности: нападающий, знающий, что фраза с заглавных, остаётся ровно там, откуда начал. Настройка есть потому, что поля паролей всё ещё требуют заглавной, а не потому, что она помогает.

Как выбрать длину

Для всего, с чем работает машина — ключей API, сессионных токенов, секретов вебхуков, паролей к базам, — экономить незачем. 32 шестнадцатеричных символа или 22 символа base64url дают 128 бит, а идти дальше стоит лишь байтов в файле настроек.

Для всего, что набирает человек, ограничение иное, и парольная фраза обычно удачнее по форме. Шесть слов из длинного списка стойче случайного пароля в двенадцать символов и куда легче даются с первого раза, а это весомее, чем кажется: секрет, в котором ошибаются при наборе, — это секрет, который записывают на бумажке.

Для цифрового кода честное прочтение таково: четыре цифры — это 13 бит, и они исчерпываются за секунды всем, что не ограничивает частоту. PIN безопасен исключительно благодаря политике блокировки вокруг него, но никогда собственной стойкостью.

Частые вопросы

Какой длины должен быть ключ API?
Ориентируйтесь на 128 бит энтропии или больше — там полный перебор перестаёт быть стратегией. Это 32 шестнадцатеричных символа, 22 символа base64url или 43 символа base64url, если нужно 256 бит. Для значения, с которым работает только программа, более длинный вариант не стоит ничего.
Это надёжнее, чем создать секрет через Math.random?
Да, и разница не в степени. Math.random — псевдослучайный генератор с малым состоянием, которое нынешние движки позволяют восстановить по короткой череде выходов, после чего известны все значения, какие он когда-либо выдаст. Этот инструмент берёт crypto.getRandomValues, CSPRNG платформы, тот же источник, из которого черпает openssl rand.
Почему вы не показываете единое «время взлома»?
Потому что такого числа нет. Один и тот же секрет недостижим за входом с ограничением частоты и быстро падает за утёкшим быстрым хешем — разница в одиннадцать порядков. Одна цифра вынуждена выбрать допущение и затем его спрятать, а читатель запоминает число, а не допущение. Три названные скорости ставят допущение туда, где его видно.
Парольные фразы слабее случайных строк?
Нет: стойкость зависит только от того, сколько слов вытянуто и из словаря какого размера, и показано и то и другое. Шесть слов из длинного списка EFF — около 77 бит, больше случайного десятисимвольного пароля. Ослабляет фразу самостоятельный выбор слов, а здесь этого нет.
Стоит ли исключать похожие символы?
Если секрет будет читать человек с экрана и набирать в другом месте — да: цена мала, а неверно прочитанный токен превращается в обращение в поддержку. Если с ним работает только программа, причин нет. Удаление 0, O, I и l опускает алфавит с 62 символов до 58 — ровно алфавит base58 — и стоит примерно одного символа длины на каждые двенадцать.
Делают ли заглавные буквы парольную фразу стойче?
Нет. Это одно и то же изменение каждый раз, и оно не добавляет к пространству фраз ни единой возможности. Показатель энтропии при включении остаётся на месте, и это правильное поведение. Настройка существует потому, что некоторые поля паролей настаивают на заглавной букве.
Отправляется ли созданное на сервер?
Нет. Порождение целиком выполняется в вашем браузере генератором случайных чисел платформы; ничто не загружается и не записывается, и всё работает без сети. Токены нигде не хранятся: перезагрузка страницы даёт новые, а прежние исчезают.