Elk wachtwoord dat uw applicatie accepteert, kan worden geraden, hergebruikt op een andere site, ingetypt op een nagemaakte aanmeldpagina of gelekt uit een back-up van de database. Passkeys lossen alle vier de problemen tegelijk op. De gebruiker meldt zich aan met de vingerafdruk, het gezicht of de pincode die de telefoon of laptop al ontgrendelt, en er staat op uw server geen geheim meer dat de moeite van het stelen waard is.
In het overzicht van de nieuwe login-componenten kregen passkeys vijf punten, en de SAML-post behandelde het aanmelden voor bedrijven met een eigen identity provider. Deze post is de wachtwoordloze kant: hoe TsgcWSAPIServer_WebAuthn passkeys registreert, gebruikers zonder gebruikersnaam aanmeldt, passkeys toont in de autofill-lijst van de browser en elke credential in uw eigen database bewaart.
Wat een passkey is
Een passkey is een sleutelpaar dat wordt gemaakt door de authenticator van de gebruiker: Windows Hello, iCloud-sleutelhanger, Google Wachtwoordmanager, een wachtwoordmanager of een FIDO2-beveiligingssleutel. De privésleutel verlaat de authenticator nooit. Uw server bewaart alleen de publieke sleutel, dus een gelekte tabel met passkeys laat niemand aanmelden. Elke handtekening is gebonden aan het domein van uw site, dus een sprekend gelijkend phishingdomein krijgt niets bruikbaars. Dat is wat phishingbestendig hier betekent.
In WebAuthn-termen is een passkey een detecteerbare credential, ook wel resident key genoemd. De authenticator bewaart de userHandle naast de privésleutel, en daarom kan de gebruiker aanmelden zonder een gebruikersnaam te typen en kan de browser de passkeys van uw site zelfstandig tonen.
Detecteerbare credentials aanvragen
WebAuthn heeft een secure context nodig, serveer uw pagina's dus via https, of vanaf localhost tijdens het ontwikkelen, en stel WebAuthnOptions.RelyingParty in op de hostnaam die de browser toont. De residentKey die de registratieopties vragen, komt uit WebAuthnOptions.DefaultOptions.Registration.DiscoverableCredential:
waundcPreferred, de standaardwaarde. De authenticator maakt een detecteerbare credential wanneer dat kan.waundcRequired. Alleen detecteerbare credentials worden geaccepteerd. Gebruik dit voor passkeys en aanmelden zonder gebruikersnaam.waundcDiscouraged. De authenticator moet een server-side credential maken, waarvoor de gebruikersnaam nodig is om aan te melden.
Een enkele registratieaanvraag kan de standaardwaarde nog steeds overschrijven met het veld discoverable_credential, ingesteld op required, preferred of discouraged.
Aanmelden zonder gebruikersnaam
Vraag de authenticatieopties op met een lege gebruikersnaam. De opties bevatten dan geen allowCredentials-lijst, de browser toont de passkeys die hij voor uw relying party heeft, en de gebruiker kiest er een. Er is niets om te typen en niets om verkeerd te typen.
Omdat de server deze keer niet de credential koos, controleert hij meer. Het antwoord moet de userHandle bevatten, de userHandle moet bij de credential horen die tekende, en een credential die uw applicatie niet kent, wordt afgewezen.
Passkeys in de autofill-lijst
Autofill, of conditional mediation, plaatst de passkeys van uw site in de suggestielijst van het gebruikersnaamveld, naast de opgeslagen wachtwoorden. Dat is de zachte manier om gebruikers te laten overstappen: de aanmeldpagina blijft werken voor mensen met een wachtwoord, en mensen met een passkey kiezen die uit de lijst.
- Voeg
autocomplete="username webauthn"toe aan het gebruikersnaamveld. - Laad
/sgcWebAuthn.jsin de pagina. Het component serveert dit zelf, vanaf het endpoint dat is ingesteld inEndpointsOptions.WebAuthn. - Vraag bij het laden van de pagina de opties zonder gebruikersnaam op en roep
startAuthentication(options, true)aan. De promise wordt opgelost zodra de gebruiker een passkey uit de lijst kiest.
Autofill vereist een browser met conditional mediation, wat vandaag de dag de actuele versies van Chrome, Edge en Safari betekent. browserSupportsWebAuthnAutofill() geeft in de andere browsers False terug, houd dus ook een knop “Aanmelden met een passkey” op de pagina beschikbaar.
Meerdere passkeys, één userHandle
Echte gebruikers hebben meer dan één passkey: een op de laptop, een op de telefoon, misschien een beveiligingssleutel voor de dag dat ze allebei kwijt zijn. Registreer elk daarvan met dezelfde gebruikersnaam. Wanneer die gebruikersnaam al credentials heeft die de server kent, hergebruikt de nieuwe registratie hun userHandle (user.id), zodat elke passkey van het account één userHandle deelt en ze allemaal naar dezelfde gebruiker leiden.
De server kent de credentials die zijn geregistreerd terwijl hij draait en de credentials die u toevoegt met AddCredential. Wanneer uw passkeys in een database leven, voegt u ze toe bij het opstarten, zodat elk account één userHandle behoudt. Wanneer een gebruiker wel een gebruikersnaam typt, vermelden de authenticatieopties elke passkey van die gebruiker in allowCredentials, en gebruikt de authenticator degene die hij heeft.
Bewaar de passkeys in uw eigen database
Het component bewaart geen credentials permanent. Uw passkeys horen naast uw gebruikerstabel, en vier events verbinden de twee:
OnWebAuthnRegistrationSuccessful. Sla het nieuwe credentialrecord op, bijvoorbeeldaCredentialRecord.AsJSON, samen met deCredentialIdenUsername.OnWebAuthnAuthenticationOptionsRequest. Voeg bij een gebruikersnaam de passkeys van die gebruiker toe aanCredentialRecords. Zonder gebruikersnaam voegt u niets toe.OnWebAuthnAuthenticationGetCredential. Wordt geactiveerd wanneer de door de browser gekozen credential niet in de lijst van de ceremony staat, wat het geval is bij elke aanmelding zonder gebruikersnaam en bij autofill. Zoek de credential op, vulaCredentialRecorden stelFoundin.OnWebAuthnAuthenticationSuccessful. Sla de nieuweSignCountenBackupStatevanaAuthentication.Credential.CredentialRecordop en maak vervolgens de sessie aan.
Een passkey opslaan en weer laden zijn twee korte handlers:
uses
sgcWebAuthn_Classes;
// registration: store the whole record, keyed by its credential id
procedure TForm1.WebAuthnWebAuthnRegistrationSuccessful(Sender: TObject;
const aRegistration: TsgcWebAuthn_Registration;
const aCredentialRecord: TsgcWebAuthn_CredentialRecord; var Accept: Boolean);
begin
DBInsertPasskey(aCredentialRecord.CredentialId, aCredentialRecord.Username,
aCredentialRecord.AsJSON);
end;
// usernameless and autofill sign-in: the browser chose the passkey,
// find it by its credential id and hand it back to the server
procedure TForm1.WebAuthnWebAuthnAuthenticationGetCredential(Sender: TObject;
const aCredentialId: string;
const aCredentialRecord: TsgcWebAuthn_CredentialRecord; var Found: Boolean);
var
vJSON: string;
begin
Found := DBFindPasskey(aCredentialId, vJSON);
if Found then
aCredentialRecord.ReadJSON(vJSON);
end;
DBInsertPasskey en DBFindPasskey staan voor uw eigen data-toegangscode. Het record dat u teruggeeft, moet de gevraagde CredentialId bevatten, en dezelfde Username wanneer de ceremony met een gebruikersnaam begon, anders mislukt het aanmelden. Bewaar ook de UserId in het opgeslagen record, omdat de server die vergelijkt met de userHandle die de authenticator stuurt. De events lopen in de verbindingsthreads van de server, bescherm gedeelde resources dus zoals u ze overal elders in de server beschermt.
Gesynchroniseerd of apparaatgebonden
De vlaggen in de authenticatordata vertellen welk type passkey u hebt ontvangen, en het credentialrecord bewaart ze als BackupEligible (de BE-vlag) en BackupState (de BS-vlag):
- BackupEligible True, BackupState True. Een gesynchroniseerde passkey, geback-upt door het platform of de wachtwoordmanager en beschikbaar op de andere apparaten van de gebruiker.
- BackupEligible True, BackupState False. Een multi-apparaat-passkey die nog niet is geback-upt.
- BackupEligible False, BackupState False. Een apparaatgebonden passkey, zoals een beveiligingssleutel of een TPM-sleutel die het apparaat nooit verlaat. Stel de gebruiker voor een tweede passkey te registreren, omdat het verliezen van dat apparaat het verliezen van de credential betekent.
Uw beleid kan ze verschillend behandelen, bijvoorbeeld door op beheerdersaccounts alleen apparaatgebonden beveiligingssleutels te accepteren. Bij elke aanmelding wijst de server een antwoord met de BS-vlag ingesteld en de BE-vlag niet ingesteld af, en een antwoord waarvan de BE-vlag afwijkt van de opgeslagen BackupEligible, omdat de eligibility van een credential nooit verandert. De nieuwe BS-vlag wordt gekopieerd naar BackupState, en OnWebAuthnAuthenticationSuccessful is de plek om die op te slaan.
De handtekeningteller en gekloonde authenticators
Sommige authenticators verhogen bij elke aanmelding een handtekeningteller. Wanneer de teller in het antwoord of de opgeslagen SignCount niet nul is, moet de teller in het antwoord groter zijn dan de opgeslagen waarde. Zo niet, dan wordt de aanmelding afgewezen, omdat twee authenticators die met dezelfde sleutel antwoorden, precies eruitzien als een gekloonde authenticator.
De controle werkt alleen als u de teller na elke aanmelding opslaat, en de opgeslagen waarde alleen maar vooruitgaat. Gesynchroniseerde passkeys melden meestal elke keer 0, wat de controle voor hen uitschakelt. Als u authenticators hebt die de teller niet betrouwbaar verhogen, stelt u WebAuthnOptions.AllowSignCountLessOrEqualStoredValue in op True om ze te accepteren. De opgeslagen waarde wordt nog steeds nooit verlaagd.
Probeer de demo
De demo Demos\26.Authentication\01.Passkeys is een complete relying party: een TsgcWebSocketHTTPServer met een gekoppelde TsgcWSAPIServer_WebAuthn, die een kleine aanmeldpagina serveert op https://localhost:5443. Het bewaart elke passkey in een eigen passkeys.json-bestand, via de vier events hierboven.
- Bouw de demo en houd libcrypto-3.dll en libssl-3.dll naast het uitvoerbare bestand. Ze staan in de demomap.
- Laat host 127.0.0.1, poort 5443 en relying party localhost staan, en klik dan op Start.
- Klik op Open Browser en accepteer het zelfondertekende testcertificaat.
- Typ een gebruikersnaam en klik op Register passkey. Registreer een tweede passkey voor dezelfde gebruikersnaam. Het formulier toont elke passkey met het type, gesynchroniseerd of apparaatgebonden, en de handtekeningteller.
- Klik op Sign in without user name en kies een passkey. De server vindt deze via
OnWebAuthnAuthenticationGetCredentialen het log toont de gebruiker. - Herlaad de pagina en klik op het gebruikersnaamveld. De passkeys verschijnen in de autofill-lijst. Kies er een om aan te melden.
- Herstart de applicatie. De passkeys worden opnieuw geladen vanuit passkeys.json, wat laat zien dat de opslag bij uw applicatie hoort en niet bij het component.
Documentatie
Waar u het kunt krijgen
TsgcWSAPIServer_WebAuthn zit in de Enterprise- en All-Access-edities van sgcWebSockets, voor Delphi en C++ Builder, en hetzelfde component maakt deel uit van sgcWebSockets .NET. Als u alleen authenticatie nodig hebt, heeft het sgcAuth-pakket het samen met de andere login-componenten. U vindt het op het SGC Auth-palet, en er verandert niets in een bestaande applicatie totdat u het op een formulier plaatst.
Lees verder
- Delphi-login met Passkeys, SAML SSO, LDAP en TOTP 2FA
- SAML Single Sign-On in Delphi met Entra ID, Okta en AD FS
- WebAuthn, passkeys en het einde van wachtwoorden
Bekijk het
Er is een korte video, “Passkeys in Delphi: passwordless login with WebAuthn”, op het eSeGeCe-kanaal. Het toont de code in de IDE, wat de back-upvlaggen en de handtekeningteller vertellen, en de demo die draait op https://localhost: een gebruiker registreert twee passkeys en meldt zich vervolgens aan zonder een gebruikersnaam te typen.
Vragen, feedback of hulp bij het toevoegen van passkeys aan uw aanmeldpagina? Neem contact op. U krijgt antwoord van de mensen die de code hebben geschreven.
