Programování

Passkey přihlásí uživatele bez hesla dvojicí klíčů vázaných na doménu

Passkey je přihlašovací údaj, který místo hesla používá dvojici kryptografických klíčů. Soukromý zůstává v zařízení, veřejný putuje na server a sám o sobě je k ničemu. Řídí to standard WebAuthn a klíč přitom patří vždy jen jedné doméně.

· 4 zhlédnutí

Hardwarový bezpečnostní klíč YubiKey v konektoru USB
Hardwarový klíč je typický roaming autentikátor: soukromý klíč nikdy neopustí čip uvnitř. Foto: Tony Webster, Wikimedia Commons (CC BY 2.0)

Heslo je sdílené tajemství. Jeden řetězec zná uživatel i server a kdo ho získá, vydává se za majitele účtu. Podle průvodce webauthn.guide stojí za ukradeným nebo slabým heslem 81 % průniků spojených s hackingem. Heslo se dá vylákat podvodnou stránkou, vytáhnout z uniklé databáze nebo uhodnout, když ho uživatel použil i jinde.

Passkey tenhle model obrací. Místo jednoho tajemství vzniká dvojice klíčů a server nikdy nedostane nic, co by šlo zneužít. Standard, který to řídí, se jmenuje WebAuthn a píše ho W3C společně s aliancí FIDO.

Dvojice klíčů místo sdíleného tajemství

Passkey stojí na asymetrické kryptografii. Při registraci autentikátor – čip v telefonu, notebooku nebo v hardwarovém klíči – vytvoří dvojici matematicky svázaných klíčů. Soukromý klíč zůstane bezpečně uložený v zařízení a nikdy ho neopustí. Veřejný klíč spolu s náhodným identifikátorem přihlašovacího údaje putuje na server, který si ho uloží k účtu.

Veřejný klíč není tajný: bez odpovídajícího soukromého klíče se s ním nedá přihlásit. To má dopad na bezpečnost celé databáze. Únik tabulky s veřejnými klíči útočníkovi nepomůže, protože chybí ta druhá polovina páru. Vývojář navíc nemá co hašovat ani solit – veřejný klíč se ukládá tak, jak přišel.

Klíč patří jen jedné doméně

Nejdůležitější vlastnost passkey je, že klíč je svázaný s konkrétním původem (origin). Při registraci server uvede pole rp.id, takzvané relying party ID – identifikátor strany, která uživatele ověřuje. Musí to být doména, na které se stránka právě nachází, nebo její nadřazená doména. Dvojice klíčů vytvořená pro webauthn.guide se nedá použít na evil-webauthn.guide.

Odtud plyne odolnost vůči phishingu. Když uživatel omylem přistane na podvodné kopii přihlašovací stránky, prohlížeč jednoduše žádný klíč pro cizí doménu nenajde a podpis nevznikne. Autentikátor totiž do podepisovaných dat vkládá skutečný původ stránky; server pak ověří, že sedí s tím jeho. U hesla stačí, aby ho uživatel napsal do falešného pole. U passkey není co napsat a podvodná doména podpis nedostane.

Registrace: navigator.credentials.create()

Přihlašovací údaj vzniká v prohlížeči voláním navigator.credentials.create(). Server k němu dodá parametry – hlavně náhodnou výzvu (challenge), která musí mít aspoň 16 bajtů a brání přehrání zachycené odpovědi.

const publicKeyCredentialCreationOptions = {
  challenge: challengeFromServer,          // >= 16 nahodnych bajtu
  rp: { name: "Moje appka", id: "example.com" },
  user: {
    id: userIdBytes,                       // ne osobni udaj
    name: "lee@example.com",
    displayName: "Lee",
  },
  pubKeyCredParams: [{ alg: -7, type: "public-key" }], // ES256
  authenticatorSelection: { authenticatorAttachment: "platform" },
  timeout: 60000,
  attestation: "none",
};

const credential = await navigator.credentials.create({
  publicKey: publicKeyCredentialCreationOptions,
});

Hodnota alg: -7 odkazuje do registru COSE a znamená ECDSA nad křivkou P-256 s hašem SHA-256 – nejběžnější volba. Pole attestation říká, jak moc server stojí o doklad původu autentikátoru; hodnota none znamená, že se o něj nezajímá, a pro většinu webů je to správně. Výsledný objekt obsahuje veřejný klíč zabalený v response.attestationObject a posílá se na server k uložení.

Přihlášení: navigator.credentials.get()

Při přihlášení uživatel dokazuje, že soukromý klíč vlastní. Server pošle novou výzvu, prohlídač zavolá navigator.credentials.get() a autentikátor výzvu podepíše – vznikne assertion, tedy podepsané tvrzení.

const publicKeyCredentialRequestOptions = {
  challenge: challengeFromServer,
  allowCredentials: [{
    id: credentialIdBytes,                 // ulozene pri registraci
    type: "public-key",
    transports: ["usb", "ble", "nfc", "internal"],
  }],
  timeout: 60000,
};

const assertion = await navigator.credentials.get({
  publicKey: publicKeyCredentialRequestOptions,
});

Server podpis ověří veřejným klíčem uloženým při registraci. Zkontroluje tři věci: že podepsaná výzva odpovídá té, kterou vydal, že sedí původ stránky a že sedí relying party ID. Když všechno platí, přihlášení je hotové – a po síti přitom nikdy neproletělo žádné tajemství.

Autentikátor v telefonu, nebo na klíčence

Autentikátory se dělí na dva druhy a výběr řídí pole authenticatorAttachment:

  • Platform autentikátor je zabudovaný v zařízení – Windows Hello, Touch ID nebo Face ID. Je po ruce, ale je vázaný na ten konkrétní přístroj.
  • Roaming (v API cross-platform) je přenosný. Typicky hardwarový klíč, který se připojí přes USB, NFC nebo Bluetooth a dá se použít na víc počítačích.

S tím souvisí ještě jedno dělení. Discoverable přihlašovací údaj (dřív se říkalo resident key) si autentikátor pamatuje celý včetně jména účtu, takže uživatel nemusí zadávat login – prohlížeč sám nabídne, s kterým účtem se přihlásit. Passkey je z definice vždy discoverable. U non-discoverable údaje se naopak klíč ukládá zašifrovaný na serveru a uživatel musí nejdřív říct, kdo je.

Kde do toho zapadá FIDO2

WebAuthn není osamocený. Je to browserová část většího celku zvaného FIDO2, který tvoří dvě specifikace. WebAuthn od W3C pokrývá API mezi webovou stránkou a prohlížečem. Druhá půlka je CTAP (Client to Authenticator Protocol) od aliance FIDO – protokol mezi prohlížečem a samotným autentikátorem. CTAP řeší, jak si spolu povídá počítač a bezpečnostní klíč nebo telefon.

Na CTAP stojí i přihlášení napříč zařízeními: passkey z telefonu jde použít při přihlášení na notebooku. Alliance FIDO tomu říká Cross-Device Authentication a jede po přenosu „hybrid“, který přes Bluetooth Low Energy ověří, že obě zařízení jsou opravdu blízko sebe. Pro vývojáře webu je podstatné, že CTAP implementují prohlížeče a autentikátory, ne server – ten vidí jen WebAuthn.

Ověření na serveru v PHP

Klientská část je jen půlka práce. Odpověď autentikátoru musí server rozebrat a ověřit, a to není triviální – specifikace popisuje u registrace vícebodovou proceduru. V PHP tuhle práci obstará knihovna web-auth/webauthn-lib od Spomky-Labs, dostupná i jako balíček pro Symfony.

composer require web-auth/webauthn-lib

Nejdřív se sestaví serializer, který převede zakódovaná data z prohlížeče na objekty, a správce podpory attestation. Pro běžný web bez potřeby dokládat model autentikátoru stačí typ none.

<?php

declare(strict_types=1);

use Webauthn\AttestationStatement\AttestationStatementSupportManager;
use Webauthn\AttestationStatement\NoneAttestationStatementSupport;
use Webauthn\Denormalizer\WebauthnSerializerFactory;

$attestationManager = AttestationStatementSupportManager::create();
$attestationManager->add(NoneAttestationStatementSupport::create());

$serializer = (new WebauthnSerializerFactory($attestationManager))->create();

Vlastní kontrolu obstarají dva validátory – jeden pro registraci (attestation), druhý pro přihlášení (assertion). Oba dostanou takzvaný Ceremony Step Manager, který v sobě nese jednotlivé ověřovací kroky.

<?php

use Webauthn\AuthenticatorAssertionResponseValidator;
use Webauthn\AuthenticatorAttestationResponseValidator;
use Webauthn\CeremonyStep\CeremonyStepManagerFactory;

$csmFactory = new CeremonyStepManagerFactory();

$attestationValidator = AuthenticatorAttestationResponseValidator::create(
    $csmFactory->creationCeremony()
);
$assertionValidator = AuthenticatorAssertionResponseValidator::create(
    $csmFactory->requestCeremony()
);

Validátor pak nad přijatou odpovědí ověří podpis, výzvu, původ i relying party ID a vrátí záznam přihlašovacího údaje k uložení. Tím se kruh uzavírá: klíč vytvořený v prohlížeči má na serveru protějšek, který ho umí zkontrolovat, aniž by kdy potřeboval znát nějaké heslo.

Zdroje

  • MDN: Web Authentication API – průběh registrace a přihlášení, discoverable klíče, platform vs. roaming autentikátor.
  • webauthn.guide – vázání klíče na doménu, ukázky volání create() a get(), údaj o 81 % průniků přes hesla.
  • FIDO Alliance: FIDO2 – že FIDO2 tvoří WebAuthn a CTAP a jak funguje přihlášení napříč zařízeními.
  • web-auth/webauthn-lib – serializer, správce attestation a oba validátory pro ověření odpovědi v PHP.

Programování

WebAuthnpasskeysFIDO2JavaScript

Diskuse

Zatím tu nikdo nediskutuje.

Diskutovat mohou přihlášení čtenáři – přihlaste se nebo si založte účet.

← zpět na výpis