В этой статье мы с тобой прос­ледим исто­рию идей бес­пароль­ной аутен­тифика­ции. Раз­берем­ся в том, что такое FIDO2, как работа­ют про­токо­лы WebAuthn и CTAP, а так­же обсу­дим их внут­ренние механиз­мы защиты и сущес­тву­ющие ата­ки. Меж­ду делом рас­кро­ем заговор мас­совой утеч­ки биомет­ричес­ких дан­ных, пос­мотрим, при чем здесь Passkeys, и попыта­емся понять, нас­коль­ко близ­ко бес­пароль­ное будущее.

Идея отка­зать­ся от паролей — чуть ли не ровес­ница самих паролей. Одна­ко пер­вые серь­езные обсужде­ния этой темы начались при­мер­но в 2004 году, ког­да Билл Гей­тс на кон­ферен­ции по безопас­ности RSA за­явил: «Нет никаких сом­нений, что со вре­менем люди будут полагать­ся на пароли все мень­ше и мень­ше. ...[пароли] прос­то не отве­чают задаче защитить что‑либо дей­стви­тель­но цен­ное». Затем был друж­ный хор голосов из IBM, Google и про­чих ком­паний, про­пове­дующих ско­рую кон­чину паролей. Но с того выс­тупле­ния Гей­тса прош­ло уже двад­цать лет, а воз и ныне там — пароли по‑преж­нему живы.

Пе­ред тем как мы дви­нем­ся даль­ше, осве­жим нес­коль­ко базовых понятий:

Нас в кон­тек­сте этой статьи инте­ресу­ет имен­но аутен­тифика­ция. Де‑фак­то стан­дартом для нее ста­ла ком­бинация логин — пароль. Но что же не так с пароля­ми?

Да и вооб­ще, что говорить, ког­да Фер­нандо Кор­бато, впер­вые реали­зовав­ший механизм паролей в 1961 году, за­являл, что он не пред­назна­чал­ся для веба и на текущий день прев­ратил­ся в кош­мар.

Беспарольная эволюция

С пароля­ми все ясно, воз­можно, они дей­стви­тель­но ста­ли боль­ше рис­ком, чем защитой, но что пред­лага­ется вза­мен?

Все началось в 1980-х, ког­да ком­пания Security Dynamics Technologies (сей­час RSA Security) соз­дала пер­вый аппа­рат­ный токен, генери­рующий одно­разо­вые пароли (OTP). В 1998 году ком­пания AT&T получи­ла па­тент на двух­фактор­ную аутен­тифика­цию, сов­местив пароль с OTP-кодом. В 2000-х получи­ла рас­простра­нение сис­тема SSO. В 2011 году в Motorola выпус­тили пер­вый смар­тфон со ска­нером отпе­чат­ка паль­цев, а еще через два года то же сде­лали и в Apple, снаб­див iPhone дат­чиком Touch ID.

Security Dynamics SecurID (OTP) и Motorola ATRIX 4G (сканер отпечатка)
Security Dynamics SecurID (OTP) и Motorola ATRIX 4G (ска­нер отпе­чат­ка)

Та­ким обра­зом, эво­люция аутен­тифика­ции соз­дала все­го три фак­тора: фак­торы зна­ния (что‑то, что ты зна­ешь), фак­торы вла­дения (что‑то, что ты име­ешь) и фак­торы свой­ства (что‑то, что харак­теризу­ет тебя). Поэто­му фун­дамен­таль­но у нас есть сле­дующие аль­тер­нативы:

Ес­ли наша задача — отка­зать­ся от паролей, то под­ходит толь­ко пос­ледний вари­ант. Будем исхо­дить из того, что бес­пароль­ная аутен­тифика­ция (БА) — это аутен­тифика­ция, которая не исполь­зует фак­тор зна­ния (пароль, сек­ретный воп­рос, пасс‑фра­за). То есть нам оста­ется выбор одно­го из двух фак­торов. Тем вре­менем мно­гофак­торная аутен­тифика­ция (MFA) пред­полага­ет исполь­зование двух и более раз­личных фак­торов. Так что БА и MFA — это раз­ные вещи, которые не сле­дует путать.

Раз­вею самое боль­шое заб­лужде­ние о БА. Любое исполь­зование биомет­рии — это локаль­ное решение про­изво­дите­ля аутен­тифика­тора, но даже в этом слу­чае ник­то никуда твои отпе­чат­ки или их про­изводные не посыла­ет и ничего ими не под­писыва­ет, это каса­ется и FIDO2, о котором пой­дет речь ниже. Мно­гие пута­ют исполь­зование биомет­рии и БА, что порож­дает мас­совую исте­рию по поводу утеч­ки биомет­ричес­ких дан­ных.

Зоопарк FIDO

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

С 2004 года были обра­зова­ны OATH, OIDF, OASIS, IDSA, OIX и дру­гие. Количес­тво орга­низа­ций и про­токо­лов рос­ло, ком­пании про­бова­ли соз­давать про­дук­ты, но ни к какому обще­му решению не приш­ли. Поэто­му в июле 2012 года PayPal, Lenovo и еще четыре ком­пании осно­вали аль­янс FIDO (Fast IDentity Online), мис­сией которо­го ста­ло «сок­ратить все­общую зависи­мость от паролей». Воп­реки рас­хожему мне­нию, Google и Microsoft не были сре­ди осно­вате­лей, а при­соеди­нились толь­ко в 2013 году. Сей­час в аль­янс FIDO вхо­дит нес­коль­ко сотен ком­паний.

На пер­вых порах ново­испе­чен­ный аль­янс объ­еди­нил два незави­симых про­екта. Во‑пер­вых, это Universal Authentication Framework (UAF), раз­работан­ный в PayPal и Validity Sensors, — про­токол, который пред­полага­ет исполь­зование биомет­ричес­ких дан­ных для аутен­тифика­ции поль­зовате­ля без пароля на сто­роне мобиль­ных устрой­ств. Во‑вто­рых, это Universal Second Factor (U2F), соз­данный в Google и Yubico. Этот про­токол пред­полага­ет исполь­зование внеш­него токена безопас­ности как вто­рого фак­тора аутен­тифика­ции, в допол­нение к паролю. В резуль­тате появи­лись две спе­цифи­кации: UAF 1.0 (8 декаб­ря 2014 года) и U2F 1.0 (9 октября 2014 года), которые мож­но условно наз­вать про­ектом FIDO 1.0.

Кон­цепт выг­лядел отлично, но ему не хва­тало уни­фика­ции, поэто­му в сен­тябре 2015 года аль­янс FIDO объ­еди­нил две спе­цифи­кации и выпус­тил пред­ложение FIDO 2.0, которое было отправ­лено в W3C. На этой осно­ве в W3C в мае 2016 года выпус­тили про­токол WebAuthn, а аль­янс FIDO пос­ле нес­коль­ких дорабо­ток в июле 2017 года зарели­зил про­токол U2F 1.2 (он же CTAP 1.0). Эту связ­ку уже мож­но наз­вать пол­ноцен­ным про­ектом FIDO 2.0.

info

W3C (World Wide Web Consortium) — это меж­дународ­ная орга­низа­ция по стан­дарти­зации веба. Есть и дру­гие подоб­ные орга­низа­ции, нап­ример WHATWG (под­держи­вает HTML) и ECMA (под­держи­вает JavaScript). Стан­дарты, выпущен­ные эти­ми орга­низа­циями, не обя­затель­ные, но общеприз­нанные.

W3C и FIDO регуляр­но выпус­кают новые ите­рации сво­их спе­цифи­каций. Так, на сегод­няшний день стан­дартом явля­ется WebAuthn (Level 2) от 2021 года и CTAP 2.1 от 2022 года, но сущес­тву­ет и новая вер­сия, которая пока на эта­пе пред­ложения.

Эволюция проектов и протоколов альянса FIDO
Эво­люция про­ектов и про­токо­лов аль­янса FIDO

На сегод­няшний день FIDO2 — это наибо­лее рас­простра­нен­ное решение аль­янса FIDO, хотя U2F так­же еще исполь­зует­ся некото­рыми веб‑сер­висами как метод 2FA, а UAF при­меня­ется в мобиль­ных при­ложе­ниях как метод под­твержде­ния дей­ствия, нап­ример тран­закции в бан­ков­ском при­ложе­нии.

Ито­го нуж­но запом­нить, что FIDO — это аль­янс, FIDO1 и FIDO2 — это про­екты, а не про­токо­лы; UAF, U2F, CTAP и WebAuthn всех вер­сий — это про­токо­лы, у которых есть одно­имен­ные спе­цифи­кации.

Архитектура FIDO2

FIDO2 позици­они­рует­ся как безопас­ный метод аутен­тифика­ции, пос­тро­енный на крип­тогра­фии пуб­лично­го клю­ча, и сос­тоит из двух час­тей — про­токо­лов WebAuthn и CTAP. Зачем два про­токо­ла? Так FIDO было лег­че фор­мализо­вать свое тво­рение: W3C выпус­кает толь­ко стан­дарты для веба, а на сто­роне устрой­ств ничего не реша­ет. Поэто­му и получил­ся такой полус­тандар­тный стан­дарт, за который отве­чают одновре­мен­но неком­мерчес­кий кон­сорци­ум (W3C) и ассо­циация ком­мерчес­ких орга­низа­ций (аль­янс FIDO).

Вна­чале ска­жу пару слов о том, что FIDO2 не пок­рыва­ет. Вне поля зре­ния про­токо­лов оста­ется то, как мы получа­ем аутен­тифика­тор (покупа­ем, или нам его выда­ют на работе), как мы акти­виру­ем аутен­тифика­тор (уста­нав­лива­ем свой отпе­чаток или код) и как хра­нят­ся наши клю­чи (шиф­рован­ное хра­нили­ще или бра­узер­ный менед­жер паролей).

Так­же FIDO2 не пок­рыва­ет Passkeys, которые сей­час начина­ют пред­лагать на некото­рых круп­ных сай­тах. Дело в том, что в пер­вых спе­цифи­каци­ях FIDO2 такого понятия не сущес­тво­вало, а поз­же сло­вом Passkeys для удобс­тва начали называть те самые крип­тогра­фичес­кие клю­чи. На прак­тике Passkeys — это одна из реали­заций клю­ча на осно­ве крип­тогра­фии, которая может исполь­зовать­ся в FIDO2 и обла­дает дру­гими полез­ными фун­кци­ями, нап­ример син­хро­низа­цией меж­ду устрой­ства­ми. Но сто­ит пом­нить, что ни WebAuthn, ни CTAP не рег­ламен­тиру­ют Passkeys. Это в дан­ный момент прос­то самое рас­простра­нен­ное решение.

Ос­новные учас­тни­ки вза­имо­дей­ствия — это:

Про­токол WebAuthn отве­чает за вза­имо­дей­ствие меж­ду JS-кли­ентом и RP, его мож­но условно наз­вать кли­ент­ским API. В боль­шинс­тве слу­чаев JS-кли­ент — это бра­узер поль­зовате­ля, но он может быть и натив­ным при­ложе­нием для Android или iOS. Для прос­тоты будем счи­тать понятия JS-кли­ент и бра­узер, а так­же веб‑сайт и RP рав­нознач­ными.

Про­токол CTAP регули­рует отно­шения меж­ду аутен­тифика­тором и устрой­ством поль­зовате­ля, его мож­но условно наз­вать API аутен­тифика­тора. Аутен­тифика­тор может быть встро­енным, нап­ример Face ID, или внеш­ним, нап­ример аппа­рат­ный токен YubiKey, но мы не будем делать меж­ду ними раз­личий.

Дан­ные CTAP может переда­вать через USB, NFC или BLE (Bluetooth Low Energy). Самые вни­матель­ные навер­няка заметят, что у нас остался про­бел во вза­имо­дей­ствии меж­ду устрой­ством поль­зовате­ля и JS-кли­ентом. К сожале­нию, каж­дая плат­форма (ОС) реша­ет этот воп­рос по‑сво­ему, здесь чет­ких стан­дартов нет.

Кон­цепту­аль­но архи­тек­тура FIDO2 при­веде­на на рисун­ке ниже, а ком­понен­ты раз­ных цве­тов зависят от раз­ных команд или даже вен­доров.

Архитектура FIDO2
Ар­хитек­тура FIDO2

Церемония регистрации

Нет‑нет, речь сей­час пой­дет не о заг­се. В FIDO2 сущес­тву­ет две церемо­нии: церемо­ния регис­тра­ции и церемо­ния аутен­тифика­ции. Давай нач­нем с пер­вой. Сра­зу ого­ворим­ся, что для чита­емос­ти я буду исполь­зовать зна­комые всем фор­маты дан­ных вмес­то бай­товых (Uint8Arrays), которые на самом деле отправ­ляют­ся в полях вызовов.

Для начала поль­зователь дол­жен вой­ти на сайт, най­ти фор­му регис­тра­ции, ввес­ти свой логин и нажать кноп­ку регис­тра­ции. Даль­ше RP фор­миру­ет зап­рос PublicKeyCredentialCreationOptions к бра­узе­ру. Этот мас­сив содер­жит:

Аттестация

Ат­теста­ция — это допол­нитель­ная про­цеду­ра безопас­ности. Ее основная цель в том, что­бы уста­новить модель аутен­тифика­тора. Ответс­твен­ной сто­роне это необ­ходимо для выпол­нения тре­бова­ний к безопас­ности, уста­нов­ленных регуля­тором.

Ес­ли аттеста­ция тре­бует­ся, то сге­нери­рован­ный пуб­личный ключ будет под­писан аттеста­цион­ным при­ват­ным клю­чом. Аттеста­цион­ный при­ват­ный ключ соз­дает­ся на аутен­тифика­торе у про­изво­дите­ля и уни­кален для каж­дой модели устрой­ства, про­изве­ден­ной в задан­ные вре­мен­ные рам­ки: нап­ример, все YubiKey 5, про­изве­ден­ные с сен­тября по декабрь 2022 года, будут иметь оди­нако­вый ключ. Сле­дом под­писан­ный пуб­личный ключ и аттеста­цион­ный сер­тификат отправ­ляют­ся к RP.

RP может про­верить аттеста­цию, а может и нет! Для уста­нов­ления доверия RP про­веря­ет цепоч­ку сер­тифика­та от под­писи про­изво­дите­ля до кор­невого сер­тифика­та GlobalSign. При этом для опре­деле­ния воз­можнос­тей аутен­тифика­тора ответс­твен­ной сто­роне необ­ходимо заг­рузить и еже­месяч­но обновлять базу MDS3, которая содер­жит дан­ные о каж­дой модели аутен­тифика­тора.

PublicKeyCredentialCreationOptions
{
"rp": {
"id": "subdomain.domain.com",
"name": "Company Name"
},
"user": {
"name": "i.ivanov@mail.ru",
"displayName": "Ivan Ivanov",
"id": "123e4567-e89b-12d3-a456-426655440000"
},
"challenge": "yGvVFCug1QLmaW4OnIYw7JONQP7XDSDNZ3SELT0PBhI",
"pubKeyCredParams": [
{
"alg": -7,
"type": "public-key"
},
{
"alg": -257,
"type": "public-key"
}
],
"authenticatorSelection": {
"authenticatorAttachment": "platform",
"residentKey": "required"
"requireResidentKey": true,
"userVerification": "discouraged"
},
"attestation": "none",
"timeout": 30000,
"excludeCredentials": [],
"extentions": []
}

Пос­ле получе­ния зап­роса бра­узер вызыва­ет фун­кцию navigator.credentials.create(), в которой про­веря­ет, что зна­чение rp.id сов­пада­ет с име­нем домена, а так­же хеширу­ет дан­ные зап­роса. На этом момен­те WebAuthn закан­чива­ется, а ОС вызыва­ет authenticatorMakeCredential у аутен­тифика­тора через CTAP-про­токол. В целом боль­шинс­тво зна­чений это­го мас­сива тебе дол­жно уже быть зна­комо, ука­жем толь­ко на clientDataHash, который явля­ется упо­мяну­тым SHA-256-хешем дан­ных зап­роса, и options, которые содер­жат тре­бова­ния по про­цеду­рам наличия поль­зовате­ля (UP) и верифи­кации поль­зовате­ля (UV) в чис­ле про­чих.

Наличие и верификация пользователя

Обе церемо­нии (регис­тра­ции и аутен­тифика­ции) тре­буют одну или обе про­цеду­ры — наличие поль­зовате­ля (UP) и верифи­кацию поль­зовате­ля (UV). UP — это обя­затель­ная базовая про­цеду­ра, которая зак­люча­ется во вза­имо­дей­ствии поль­зовате­ля с аутен­тифика­тором. Нап­ример, нуж­но нажать на кноп­ку. UV — это более слож­ная, но и опци­ональ­ная про­цеду­ра, ког­да аутен­тифика­тор убеж­дает­ся, что поль­зователь авто­ризо­ван его исполь­зовать. Нап­ример, может быть нуж­но ввес­ти пин или пре­дос­тавить биомет­рию.

Спе­цифи­кации WebAuthn и CTAP не опре­деля­ют эти про­цеду­ры стро­го, говоря о неко­ем «авто­риза­цион­ном жес­те», который каж­дый про­изво­дитель может понять по‑сво­ему. Для FIDO2 раз­ница в том, что UV поз­воля­ет отли­чить раз­ных поль­зовате­лей одно­го аутен­тифика­тора, а UP — нет.

authenticatorMakeCredential
{
"clientDataHash": "LNeTz6C...oYH2el7Mz1NsKQQF3Zq9ruMdVE",
"rp": {
"id": "subdomain.domain.com",
"name": "Company Name"
},
"user": {
"name": "i.ivanov@mail.ru",
"displayName": "Ivan Ivanov",
"id": "123e4567-e89b-12d3-a456-426655440000"
},
"pubKeyCredParams": [
{
"alg": -7,
"type": "public-key"
},
{
"alg": -257,
"type": "public-key"
}
],
"excludeCredentials": [],
"extentions": [],
"options": {
"rk": true,
"up": true,
"uv": false
}
}

Пос­ле получе­ния зап­роса аутен­тифика­тор соз­дает пару клю­чей и про­изво­дит дру­гие дей­ствия, все­го в алго­рит­ме 19 шагов. В резуль­тате аутен­тифика­тор воз­вра­щает опе­раци­онной сис­теме по CTAP объ­ект AuthenticatorAttestationResponse. ОС переда­ет его ску­чающей фун­кции navigator.credentials.create(), которая фор­миру­ет PublicKeyCredential и закан­чива­ет свой цикл.

Воз­вра­щаемый аутен­тифика­тором AuthenticatorAttestationResponse час­то называ­ют прос­то Attestation, поэто­му иног­да мож­но услы­шать, что весь про­цесс регис­тра­ции тоже носит наз­вание Attestation (аттеста­ция), но это ско­рее оби­ход­ное наз­вание, которое не сле­дует путать с про­цес­сом уста­нов­ления модели аутен­тифика­тора опи­сан­ным выше. Фор­маты AuthenticatorAttestationResponse и PublicKeyCredential прак­тичес­ки иден­тичны, поэто­му рас­смот­рим толь­ко пос­ледний:

PublicKeyCredential
{
"id": "AV19-ikcu3tKuMxfFnRZ9gU6tnDH6QqzYwUg",
"response": {
"clientDataJSON": "ew0KCSJ0eXBl...A0KCSJjaGFsbGVuZ2UiIDogInlHdl",
"attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdX...jLvw6oUiKGB"
"authData": "LNeTz6C0...hSIoYH2el7Mz1NsKQQF3Zq9ruMdV"
},
"transports": {
"internal"
},
},
"clientExtensionResults": {
"credProps": {
"rk": true
}
}
}

Ком­понен­ты мас­сива в деталях:

"clientDataJSON": {
"challenge": "qNqrdXUrk5S7dCM1MAYH3qSVDXznb-6prQoGqiACR10",
"origin": "https://subdomain.domain.com",
"type": "webauthn.create",
"crossOrigin": false,
"tokenBinding": {
"status": "unsupported"
}
}
"attestationObject": {
"fmt": "none",
"attStmt": [],
"authData": "LNeTz6C0...hSIoYH2el7Mz1NsKQQF3Zq9ruMdV"
}
"authData": {
"rpIdHash": "LNeTz6C0GMu_DqhSIoYH2el7Mz1NsKQQF3Zq9ruMdVE",
"flags": {
"up": true,
"uv": false,
"at": true,
"ed": false
},
"signCount": 0,
"attestedCredentialData": {
"aaguid": "YCiwF7HUTAK0s6_Nr8lrsg",
"credentialId": "AV19-ikcu3tKuMxfFnRZ9gU6tnDH6QqzYwUg",
"credentialPublicKey": "pAEDAzkWZFf4weLZ...kjWm6vNvT-JWMqe39MBAAE"
}
}

На финаль­ном эта­пе бра­узер отправ­ляет PublicKeyCredential к RP. RP про­изво­дит верифи­кацию получен­ных дан­ных, нап­ример про­веря­ет, соот­ветс­тву­ют ли получен­ные клю­чи и алго­рит­мы заяв­ленным тре­бова­ниям, про­водит аттеста­цию. Все­го спе­цифи­кация WebAuthn пред­полага­ет алго­ритм из 19 шагов. В слу­чае успе­ха RP сох­раня­ет credentialID и пуб­личный ключ с метадан­ными у себя в базе. Далее поль­зователь чаще все­го перенап­равля­ется на стра­ницу логина, что­бы вой­ти.

Церемония регистрации
Це­ремо­ния регис­тра­ции

Церемония аутентификации

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

Conditional UI

Conditional UI (он же Passkeys autofill) — это воз­можность авто­запол­нения. Это не часть WebAuthn, а допол­нитель­ная раз­работ­ка, которую RP может внед­рить на фрон­те для удобс­тва поль­зования.

Conditional UI работа­ет толь­ко для аутен­тифика­ции и под­держи­вает­ся боль­шинс­твом бра­узе­ров или встро­енны­ми вызова­ми ОС для натив­ных при­ложе­ний. Conditional UI работа­ет толь­ко с обна­ружи­ваемы­ми клю­чами (см. ниже), а так­же может всту­пать в кон­фликт с воз­можнос­тями авто­запол­нения менед­жеров паролей. При этом он не тре­бует уста­нов­ки пла­гинов на сто­роне поль­зовате­ля.

RP фор­миру­ет зап­рос PublicKeyCredentialRequestOptions к бра­узе­ру, который содер­жит:

Обнаруживаемые и необнаруживаемые ключи

Су­щес­тву­ет два типа клю­чей — обна­ружи­ваемые (discoverable или resident) и необ­наружи­ваемые (non-discoverable или non-resident). Они раз­лича­ются по мес­ту хра­нения и спо­собу зап­роса. В FIDO2 по умол­чанию соз­дают­ся обна­ружи­ваемые клю­чи.

Об­наружи­ваемые клю­чи хра­нят­ся на аутен­тифика­торе, а при логине аутен­тифика­тор пред­лага­ет поль­зовате­лю выб­рать, какой из клю­чей для домена (rp.id) исполь­зовать.

Не­обна­ружи­ваемые клю­чи не хра­нят­ся на аутен­тифика­торе, а соз­дают­ся как про­изводное от мас­тер‑клю­ча аутен­тифика­тора и сид‑зна­чения. При логине RP отправ­ляет аутен­тифика­тору иден­тифика­тор клю­ча и сид‑зна­чение, а аутен­тифика­тор вос­созда­ет ключ и под­писыва­ет задачу. Так как ключ дос­тупен в памяти аутен­тифика­тора толь­ко опре­делен­ное вре­мя, его еще называ­ют эфе­мер­ным, а сам аутен­тифика­тор не зна­ет, что ключ сущес­тву­ет, если ему не ска­зать об этом.

PublicKeyCredentialRequestOptions
{
"publicKeyCredentialRequestOptions": {
"challenge": "kYhXBWX0HO5GstIS02yPJVhiZ0jZLH7PpC4tzJI-ZcA=",
"timeout": 30000,
"rpId": "subdomain.domain.com",
"userVerification": "discouraged",
"allowCredentials": [
{
"id": "X9FrwMfmzj...",
"type": "public-key"
}
],
"extensions": []
}
}

Да­лее бра­узер вызыва­ет фун­кцию navigator.credentials.get(), в которой про­веря­ет, что зна­чение rp.id сов­пада­ет с доменом вза­имо­дей­ствия, а так­же хеширу­ет дан­ные зап­роса. На этом момен­те WebAuthn опять закан­чива­ется, а ОС вызыва­ет фун­кцию authenticatorGetAssertion аутен­тифика­тора через про­токол CTAP.

По­лучив зап­рос, аутен­тифика­тор находит у себя при­ват­ный ключ и выпол­няет дру­гие дей­ствия, вклю­чая вза­имо­дей­ствие с поль­зовате­лем. Весь алго­ритм пред­полага­ет 13 шагов. В резуль­тате аутен­тифика­тор по CTAP воз­вра­щает опе­раци­онной сис­теме объ­ект AuthenticatorAssertionResponse, который та переда­ет в navigator.credentials.get(). Эта фун­кция фор­миру­ет PublicKeyCredential и закан­чива­ет свой цикл.

Воз­вра­щаемый аутен­тифика­тором AuthenticatorAssertionResponse час­то называ­ют прос­то Assertion, поэто­му иног­да мож­но услы­шать, что весь про­цесс аутен­тифика­ции тоже носит наз­вание Assertion (под­твержде­ние) — по ана­логии с тем, как регис­тра­цию час­то называ­ют аттеста­цией.

Для наг­ляднос­ти давай пос­мотрим на раз­ницу в отправ­ляемых дан­ных мас­сива PublicKeyCredential при регис­тра­ции и аутен­тифика­ции.

PublicKeyCredential
{
"id": "X9FrwMfmzj...",
"response": {
"authenticatorData": {
"authData": {
"rpIdHash": "LNeTz6C0GMu_DqhSIoYH2el7Mz1NsKQQF3Zq9ruMdVE",
"flags": {
"up": true,
"uv": false,
"at": false,
"ed": false
},
"signCount": 1,
},
"clientDataJSON": {
"challenge": "kYhXBWX0HO5GstIS02yPJVhiZ0jZLH7PpC4tzJI-ZcA",
"origin": "https://subdomain.domain.com",
"type": "webauthn.get"
},
"signature": "MEUCIQDNrG...HD3UWA",
"userHandle": "123e4567-e89b-12d3-a456-426655440000"
},
"clientExtensionResults": []
}

На финаль­ном эта­пе бра­узер отправ­ляет PublicKeyCredential ответс­твен­ной сто­роне. Она верифи­циру­ет получен­ные дан­ные, нап­ример про­веря­ет, сов­пада­ет ли challenge. Весь алго­ритм про­вер­ки в спе­цифи­кации WebAuthn — это 23 шага. В слу­чае успе­ха RP отправ­ляет поль­зовате­лю сес­сион­ный cookie.

Церемония аутентификации
Це­ремо­ния аутен­тифика­ции

Атаки на FIDO2

FIDO2 пред­полага­ет защиту не толь­ко от сла­бых паролей, но и от фишин­говых и MitM-атак, а так­же повышен­ную при­ват­ность дан­ных поль­зовате­ля. Более того, даже если хакеры взло­мают сер­вер ком­пании, поль­зовате­ли не будут ском­про­мети­рова­ны. Давай раз­берем­ся со все­ми эти­ми утвер­жде­ниями по оче­реди.

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

Во‑вто­рых, фишинг. Здесь тоже сог­ласим­ся с раз­работ­чиками стан­дарта: мы пом­ним, что при регис­тра­ции RP обме­нива­ется с бра­узе­ром зна­чени­ем rp.id, к которо­му при­вязы­вает­ся при­ват­ный ключ. Поэто­му, даже если ты вой­дешь не в ту дверь, аутен­тифика­тор ничего не под­пишет. Но ничего прин­ципи­аль­но нового здесь нет, любой менед­жер паролей обла­дает такой фун­кци­ей.

Пе­рехо­дим к самому инте­рес­ному. Ата­ка син­хро­низи­рован­ного логина спе­цифич­на для FIDO2, но опи­рает­ся на ста­рый доб­рый клик­дже­кинг (clickjacking). Если на уяз­вимом сай­те уста­новить невиди­мый фрейм, внут­ри которо­го помес­тить панель для логина с дру­гого домена, где поль­зователь тоже зарегис­три­рован, про­изой­дут обе аутен­тифика­ции: та, которую поль­зователь зап­ланиро­вал, и та, которую зап­ланиро­вал зло­умыш­ленник.

Кто‑то обя­затель­но ска­жет, что аутен­тифика­тор показы­вает поль­зовате­лю уве­дом­ление о том, на какой ресурс он заходит. Одна­ко Тарун Кумар Ядав отме­тил в сво­ем иссле­дова­нии (PDF), что толь­ко один из двад­цати поль­зовате­лей про­веря­ет домен­ное имя в поп‑апе, а двой­ное нажатие на аутен­тифика­тор ник­то не счи­тает злов­редным.

Еще одна инте­рес­ная ата­ка — это кло­ниро­вание аутен­тифика­тора. Сей­час это слож­ная задача, но не невыпол­нимая. Так, в 2024 году спе­циалис­там NinjaLab уда­лось успешно кло­ниро­вать аппа­рат­ные клю­чи YubiKey 5.

У FIDO2 есть защита от подоб­ных типов атак — тот самый signCount, который при несов­падении на сто­роне аутен­тифика­тора и RP не поз­волит успешно завер­шить вход. Но этот механизм защиты лег­ко обхо­дит­ся. Дело в том, что аутен­тифика­тор уве­личи­вает signCount пос­ле каж­дой успешной под­писи задачи, а RP — пос­ле каж­дой успешной аутен­тифика­ции, то есть, по сути, обе сто­роны дей­ству­ют незави­симо.

Пред­положим, что зло­умыш­ленник ско­пиро­вал твой аппа­рат­ный токен, ког­да signCount равен 200, обоз­начим это как A = 200, L = 200 от слов Assertion на сто­роне аутен­тифика­тора и Login на сто­роне RP. Ход ата­ки про­демонс­три­рован в таб­лице ниже. Зло­умыш­ленник успешно вхо­дит на ресурс, при этом A уве­личи­вает­ся толь­ко у него, а L — у всех учас­тни­ков.

Ког­да поль­зователь поп­робу­ет залоги­нить­ся, получит ошиб­ку, так как A и L сов­падать не будут, но аутен­тифика­тор поль­зовате­ля успешно под­пишет задачу и уве­личит signCount, а RP не осу­щес­твит успешно­го логина и оста­вит зна­чение signCount неиз­менным. Соот­ветс­твен­но, оба зна­чения срав­няют­ся и при вто­рой попыт­ке логина поль­зователь успешно зай­дет в акка­унт. К сожале­нию, сай­ты показы­вают толь­ко общее сооб­щение об ошиб­ке, час­то про­ся поль­зовате­ля пов­торить вход, ког­да ошиб­ки уже не воз­никнет.

Этап Зло­умыш­ленник Жер­тва
1. Изна­чаль­ное зна­чение A = 200, L = 200 A = 200, L = 200
2. Зло­умыш­ленник вошел A = 201, L = 201 A = 200, L = 201
3. Жер­тва пыта­ется вой­ти A = 201, L = 201 Ошиб­ка, так как A != L
4. Жер­тва пыта­ется вой­ти сно­ва A = 201, L = 201 A = 201, L = 201

Мож­но еще вспом­нить прос­тую ата­ку с кра­жей сес­сии (session hijacking). Даже пос­ле супер‑пупер‑защищен­ной аутен­тифика­ции мы оста­емся наеди­не с про­токо­лом HTTP, который не хра­нит сос­тояния и опи­рает­ся на cookie-фай­лы. Поэто­му, если сайт под­вержен, нап­ример, XSS, мож­но пол­ностью зав­ладеть сес­сией поль­зовате­ля. Да и MitM-ата­ки никак не пре­дот­вра­щают­ся.

FIDO2 опи­рает­ся на TLS-соеди­нение, при этом MitM с началом при­мене­ния TLS никуда не про­пали. Так что не сто­ит наде­ять­ся на отсутс­твие проб­лем и в дан­ном слу­чае.

Го­ворить о слу­чаях, ког­да кор­невой сер­тификат находит­ся у тво­ей орга­низа­ции или у государс­твен­ного агентства, я думаю, даже не сто­ит. Ситу­ацию мог­ла бы спас­ти тех­нология Token Binding (RFC 8471), которая при­вязы­вает TLS-соеди­нение к бра­узе­ру поль­зовате­ля, но, к сожале­нию, в FIDO2 она необя­затель­на, а бра­узе­ры ее прак­тичес­ки не под­держи­вают.

Как видим, у FIDO2 есть внут­ренние уяз­вимос­ти про­токо­лов и он не дает защиту от мно­гих атак. Нап­ример, в иссле­дова­нии Михала Кеп­ков­ски при­веден при­мер уда­лен­ной ата­ки по вре­мени для опре­деле­ния клю­чей, име­ющих­ся на аутен­тифика­торе. Иссле­дова­тель так­же ука­зыва­ет и на то, что про­цеду­ра UP лег­ко симули­рует­ся, а, пос­коль­ку боль­шинс­тво ответс­твен­ных сто­рон не тре­буют UV, это соз­дает еще один век­тор обхо­да механиз­мов защиты FIDO2.

Та­рун Кумар Ядав так­же ука­зыва­ет, что 47% бра­узер­ных рас­ширений из Chrome Web Store могут осу­щес­твить как минимум пять спе­цифич­ных для FIDO2 атак, хотя дей­стви­тель­но вре­донос­ных рас­ширений, нацелен­ных на такие ата­ки, най­дено не было.

От­мечу так­же, что воп­рос при­ват­ности FIDO2 оста­ется откры­тым — поль­зователь по‑преж­нему оставля­ет на сто­роне RP тон­ну лич­ных дан­ных, да и про­фили­рова­ние стан­дар­тны­ми метода­ми никуда не делось.

Выводы

Сре­ди оче­вид­ных плю­сов FIDO2 — защита от двух самых рас­простра­нен­ных кибера­так: фишин­га и под­бора паролей. Это, безус­ловно, вооду­шев­ляет, одна­ко не сто­ит забывать, что сов­мес­тное исполь­зование тех же самых менед­жеров паролей и 2FA дает прак­тичес­ки такой же эффект.

Ко­неч­но, БА выг­лядит про­ще и удоб­нее — от поль­зовате­ля не тре­бует­ся что‑то вво­дить или при­думы­вать, все, за исклю­чени­ем нес­коль­ких нажатий, про­исхо­дит авто­мати­чес­ки. Так­же на сто­роне FIDO2 тот факт, что эта тех­нология под­держи­вает­ся все­ми основны­ми бра­узе­рами и ОС.

Пусть бес­пароль­ная аутен­тифика­ция — это кру­то, но боль­шинс­тво веб‑ресур­сов ее все‑таки не вво­дят. Ком­пании не видят смыс­ла, потому что, во‑пер­вых, сей­час и так все прек­расно работа­ет с пароля­ми, во‑вто­рых, поль­зовате­ли при­вык­ли к паролям, а в‑треть­их, БА — это новый про­цесс, и при­дет­ся его сог­ласовы­вать, выбирать софт и пла­тить раз­работ­чикам. То есть БА для биз­неса — это дол­го, дорого и неин­терес­но.

Бо­лее того, FIDO2 — это толь­ко про­токо­лы, а от ответс­твен­ной сто­роны зависит их реали­зация, пра­виль­ная нас­трой­ка тре­бова­ний к аутен­тифика­торам, под­дер­жка механиз­мов вос­ста­нов­ления дос­тупа и хра­нения дан­ных. Это нак­ладыва­ет на биз­нес допол­нитель­ную ответс­твен­ность и соз­дает новые рис­ки. Воз­можно, имен­но из‑за это­го БА пока дер­жится на голом энту­зиаз­ме отдель­ных ком­паний, а серь­езные сдви­ги могут про­изой­ти толь­ко пос­ле вме­шатель­ства регуля­торов.

Пусть БА — это удоб­но, но понима­ет ли сред­ний юзер, как она работа­ет, где хра­нят­ся вол­шебные клю­чи и чего делать нель­зя? По край­ней мере, сей­час люди в кур­се, что пароли дол­жны быть слож­ными и ими не нуж­но делить­ся, что есть фишинг, и, по сути, поль­зовате­ли соз­натель­но идут на риск, не соб­людая пра­вила информа­цион­ной гиги­ены. А БА и аппа­рат­ные токены — это для людей прос­то магия.

Для при­мера мож­но вспом­нить, как отно­сят­ся к КЭП генераль­ного дирек­тора в неболь­ших ком­пани­ях: аппа­рат­ный токен с ней гуля­ет по все­му офи­су и каж­дый сот­рудник под­писыва­ет и при­нима­ет докумен­ты под­писью генераль­ного дирек­тора.

Па­роль лег­ко передать доверен­ному лицу, и все уме­ют это делать. С БА появят­ся новые спо­собы делить­ся дос­тупом, но, что­бы они работа­ли, нуж­на сов­мести­мость меж­ду устрой­ства­ми. Да и сами поль­зовате­ли дол­жны знать о том, как работа­ет эта фун­кция, и уметь ей поль­зовать­ся.

С самим FIDO2 тоже не все так глад­ко. В про­токо­лах CTAP и WebAuthn есть внут­ренние уяз­вимос­ти и сла­бые механиз­мы защиты, а меж­ду ними — прос­лой­ка в виде ОС, которая дела­ет нас зависи­мыми от треть­ей сто­роны, чьи дей­ствия FIDO2 не регули­рует.

FIDO2 по‑преж­нему под­вержен MitM и кра­жам сес­сии, да и в пла­не при­ват­ности для поль­зовате­ля он не дает новых надежд. Аппа­рат­ные токены не обновля­ются, так как это несет риск их заразить, поэто­му на поль­зовате­ля пада­ет бре­мя не толь­ко выбора и покуп­ки железа, но и его замены в слу­чае кри­тичес­кой уяз­вимос­ти.

БА ради удобс­тва пред­лага­ет отка­зать­ся от пароля и исполь­зовать толь­ко один фак­тор. Получа­ется, что мы ска­тыва­емся от при­выч­ного 2FA, где есть и пароль и код OTP к 1FA, где есть толь­ко аутен­тифика­тор. Конеч­но, мож­но реали­зовать мно­гофак­торную БА и исполь­зовать ещё, нап­ример, биомет­рию, но в чем тог­да раз­ница с нынеш­ней пароль­ной аутен­тифика­цией? Все рав­но ведь нуж­но будет впи­сывать раз­ные коды и даже тас­кать с собой аппа­рат­ный токен, что повыша­ет риск его потерять.

FIDO2, как и идея БА, конеч­но, выг­лядят очень мно­гообе­щающе. Про­дела­на огромная работа, круп­ные ком­пании сог­ласились дей­ство­вать вмес­те ради бла­город­ной цели — сде­лать жизнь про­ще и безопас­нее. И пус­кай начало бес­пароль­ной револю­ции уже положе­но, но работы пред­сто­ит еще очень мно­го. А зна­чит, в обоз­римом будущем мы вряд ли отка­жем­ся от паролей.