spinny:~/writing $ vim passkeys-webauthn-passwordless-authentication.md
1~2Пароли — одна из тех вещей, которые мы стали нормой только потому, что живем с ними уже много лет. Пользователи забывают их, используют повторно, пишут там, где не следует. Команды должны управлять сбросами, политиками, хэшами, утечками, фишингом и поддержкой.3~4passkey не делают аутентификацию идеальной, но устраняют огромную проблему: серверу больше не нужно хранить секрет, доступный пользователю.5~6## Что происходит на самом деле7~8passkey — это учетные данные, основанные на WebAuthn. Когда пользователь создает его, устройство генерирует пару ключей:9~10- закрытый ключ, который остается на устройстве или в менеджере паролей;11- открытый ключ, который может сохранить сервер.12~13При входе сервер не спрашивает "скажи мне пароль". Отправьте случайный вызов. Устройство подписывает его закрытым ключом. Сервер проверяет подпись с помощью открытого ключа.14~15И это приятно: если база данных украдена, внутри нее нет паролей, которые можно было бы взломать. А если пользователь окажется в поддельном домене, passkey недействителен для этого домена. Это не просто удобство, это конкретная защита от фишинга.16~17## Браузер действует как мост18~19В браузере есть два основных API:20~21- `navigator.credentials.create()`, чтобы создать passkey;22- `navigator.credentials.get()`, чтобы использовать его во время входа в систему.23~24Но важная логика находится на сервере. Сервер должен сгенерировать запрос, временно сохранить его, проверить ответ, проверить источник и Relying Party ID, а затем создать сеанс.25~26Клиентская часть должна быть почти скучной:27~28```typescript29const options = await fetch('/api/passkeys/login/options').then((r) => r.json());30~31const credential = await navigator.credentials.get({32 publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(options),33});34~35await fetch('/api/passkeys/login/verify', {36 method: 'POST',37 headers: { 'Content-Type': 'application/json' },38 body: JSON.stringify(credential?.toJSON()),39});40```41~42Если вы обнаружите, что используете шифрование WebAuthn вручную, остановитесь. Используйте надежную серверную библиотеку. Ошибки здесь — это не «милые баги», а дыры в аутентификации.43~44## Что сохранять в базе данных45~46Нет нужды спасать полмира. Обычно достаточно:47~48- идентификатор удостоверения;49- открытый ключ;50- подключенный пользователь;51- любой счетчик проверки или метаданные;52- транспорты, если это полезно для UX;53- имя, выбранное пользователем;54- дата создания и последнее использование.55~56Минимальная таблица может выглядеть так:57~58```sql59create table passkey_credentials (60 id uuid primary key default gen_random_uuid(),61 user_id uuid not null references users(id),62 credential_id text not null unique,63 public_key text not null,64 name text,65 created_at timestamptz not null default now(),66 last_used_at timestamptz67);68```69~70Затем я бы добавил журналы аудита и уведомления: если кто-то создаст новый passkey в моей учетной записи, я хочу об этом знать.71~72## UX важнее, чем демо73~74Демо-версия passkey всегда прекрасна: вы нажимаете, Face ID, и вы в игре. Реальный продукт сложнее.75~76Кто-то меняет телефон. Кто-то использует заблокированный компьютер компании. Некоторые люди не понимают, почему браузер предлагает passkey. Кто-то теряет доступ к своему устройству.77~78Вот почему я бы не стал начинать с фразы «с сегодняшнего дня больше никаких паролей для всех». Я бы начал так:79~801. passkey опционально для внутренних пользователей;812. предложение создать его после успешного входа в систему;823. страница учетной записи для переименования и удаления passkey;834. четкий резервный вариант;845. постепенное внедрение в основной логин.85~86Текст в интерфейсе должен быть простым. «Использовать экран блокировки вашего устройства» лучше, чем «аутентифицироваться с использованием резидентских учетных данных FIDO2».87~88## Ошибки, которых я бы избегал89~90Не создавайте проблем для клиента. Задание создается на сервере и должно быть проверено только один раз.91~92Не доверяйте только идентификатору учетных данных. Вам необходимо подтвердить подпись, запрос, происхождение и Relying Party ID.93~94Не удаляйте резервные варианты, пока не будет выполнен хороший процесс восстановления. Функция «Беспароль» не обязательно означает «если ты потеряешь свой телефон, ты выйдешь навсегда».95~96Не рассматривайте passkey как кнопку исключительно внешнего интерфейса. Скрытие кнопки не является безопасностью: настоящая проверка происходит на стороне сервера.97~98## Passkey-первый или passkey-дружественный?99~100О новом продукте вы можете подумать passkey в первую очередь. Для существующего приложения я предпочитаю использовать passkey: добавьте passkey в качестве рекомендуемого метода, измерьте успех и проблемы, а затем спокойно уменьшите вес пароля.101~102Идеальная миграция не ощущается. Пользователь обнаруживает, что вход в систему стал проще, а не то, что компания изменила протокол аутентификации.103~104## Заключение105~106passkey интересны тем, что они одновременно улучшают безопасность и UX, что бывает редко. Они не волшебная палочка: восстановление, совместимость, поддержка и развертывание еще предстоит тщательно спроектировать.107~108Но основные изменения сильны. Перестаньте просить пользователей изобретать и защищать секреты. Вы позволяете устройству подписать криптографическое доказательство, привязанное к вашему домену. Меньше человеческой памяти, меньше фишинга, меньше сбросов паролей. Я бы сказал, что к ним стоит отнестись серьезно.109~110## Источники111~112- [MDN: Passkeys](https://developer.mozilla.org/en-US/docs/Web/Security/Authentication/Passkeys)113- [MDN: Web Authentication API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API)114- [W3C: Web Authentication](https://www.w3.org/TR/webauthn-3/)115- [passkeys.dev](https://passkeys.dev/)116~
NORMAL · passkeys-webauthn-passwordless-authentication.md [readonly]116 lines · :q to close