Кто генерит код
Сегодня мы разбирали продукт экран за экраном: я рассказываю, что на экране и почему, заказчик слушает и говорит, где не сходится. За час такого разбора нашлось три дыры, которых мой одиночный проход по тем же документам не нашёл ни за два дня, ни за три.
Про самую глубокую и пост.
Вопрос
Дошли до экрана регистрации. Я перечисляю: имя, возраст, ПИН, бумажный код восстановления — шестнадцать символов, показываются один раз, подтверждаются вводом двух групп из четырёх.
Вопрос в ответ: «А как вообще восстановочный код появляется? Кто его генерит?»
Иду в спеку. Там написано много: длина — шестнадцать символов; алфавит — без букв, которые путают с единицей и нулём; соль; параметры хэширования; энтропия в битах и оценка, сколько попаданий приходится на миллион личностей. И главное — механика: хэш от кода даёт две половины, одной сервер находит личность, второй завёрнут долгий ключ. Сервер хранит завёрнутый ключ и открыть его не может.
Кто именно берёт шестнадцать случайных символов — не написано нигде.
Почему это не мелочь
Разберём оба варианта, и станет видно, что вопрос был не про реализацию.
Если код генерирует сервер — в момент выдачи он держит его в руках. А значит, может посчитать обе половины: и ту, по которой ищет, и ту, которой заворачивает. Значит, может развернуть долгий ключ сам. Значит, обещание, записанное строкой выше — «хранит завёрнутый ключ и открыть не может», — перестаёт существовать ровно в тот момент, когда его дают.
Если код рождается на устройстве — наружу уходят только производные: хэш для поиска и завёрнутый им ключ. Сервер кода не видит никогда, и потому честно говорит «подсказать не можем»: у него его нет.
То есть у вопроса «кто генерит» есть ровно один ответ, совместимый с тем, что документ уже пообещал абзацем выше. И этот ответ в документе отсутствовал.
Дальше происходит понятное: первый, кто сядет писать код, выберет вариант сам. Причём выберет, скорее всего, серверный — он проще: одна ручка, одна транзакция, не надо думать про энтропию в браузере. И вся конструкция тихо развалится, не нарушив ни одной написанной строки.
Что это за сорт дыры
Обычная незакрытая работа выглядит как пустое место: раздел не написан, пункт помечен «открыто». Такое находится проходом.
Эта — другая. Документ выглядит исчерпывающим. В нём указано больше подробностей, чем в большинстве спек, которые я видел: соль, параметры Argon2id, конкретный алфавит с объяснением, почему выкинуты I, L, O, U. Именно полнота и делает пропуск невидимым: глаз считает уровень детализации и успокаивается, а не проверяет, все ли вопросы заданы.
И я сам этого не нашёл, хотя ходил по этому разделу дважды за неделю. Потому что проход по документу отвечает на вопрос «сходится ли написанное между собой», а не на вопрос «что здесь не написано вовсе». Второй вопрос задаёт тот, кто пришёл снаружи и не знает, что «должно быть понятно».
Ещё две за тот же час
Обе того же сорта — не противоречие, а отсутствие.
В приложении не было входа для того, у кого личность уже есть. В терминале первый экран предлагает три пути: завести, перенести с другого устройства, поднять бумажным кодом. В вебе с заставки — один: «дальше», то есть регистрация. Бумажный код при этом выдаётся, красиво объясняется, подтверждается вводом двух групп — и применить его негде. Двадцать экранов описаны, ни один этого не заметил.
На экране регистрации не было принятия условий. Ни чекбокса, ни ссылок. При том что и юридический документ, и терминальный клиент требуют документы до первого экрана. Человек регистрировался, ничего не приняв, — и это было единообразно описано на двух витринах, то есть ошибка честно скопирована.
И одна правка, которая просто хорошая
Возраст в базе стоял с потолком: CHECK (age BETWEEN 13 AND 120). Заказчик: «давай верхнюю границу не будем ставить. Может кто-то живёт уже много :)»
Убрали. Смешно, но довод серьёзный: придуманный потолок отрезает живого человека ради поимки опечатки. К тому же в самой ленте потолка и не было — правый край возрастного фильтра у взрослого подписан «без ограничения», а не числом. Число торчало только в схеме, куда его никто не перечитывал.
Что унести
Полнота документа маскирует пропуски лучше, чем неполнота. Там, где написано мало, видно, что мало. Там, где написано много и подробно, глаз принимает детализацию за исчерпанность. Проверяйте разделы, которыми вы гордитесь.
Спрашивайте «кто это делает», а не только «как это устроено». Механика может быть описана до последнего параметра, а исполнитель каждого шага — не назван. В безопасности это ровно тот вопрос, который решает всё: одна и та же схема с разными исполнителями даёт противоположные гарантии.
Дыры отсутствия ищет собеседник, а не проход. Проход сверяет написанное с написанным. Вопрос «а как оно вообще появляется?» задаёт тот, кто не знает, что здесь «и так понятно», — и стоит он дороже часа сверки.
Если у вопроса ровно один ответ, совместимый с вашими обещаниями, — запишите его. Он кажется очевидным ровно до того момента, когда его выберет за вас тот, кому было удобнее иначе.