Генератор токенов
Создаёт секреты через криптографический генератор браузера — 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 — и стоит примерно одного символа длины на каждые двенадцать.
- Делают ли заглавные буквы парольную фразу стойче?
- Нет. Это одно и то же изменение каждый раз, и оно не добавляет к пространству фраз ни единой возможности. Показатель энтропии при включении остаётся на месте, и это правильное поведение. Настройка существует потому, что некоторые поля паролей настаивают на заглавной букве.
- Отправляется ли созданное на сервер?
- Нет. Порождение целиком выполняется в вашем браузере генератором случайных чисел платформы; ничто не загружается и не записывается, и всё работает без сети. Токены нигде не хранятся: перезагрузка страницы даёт новые, а прежние исчезают.