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- ID облікових даних;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