2024/07/01

WebAuthn

WebAuthn 是 Web 與 Authentication 結合的縮寫,也就是 Web Authentication,用在網頁身份認證。這個規格是由 W3C 與 FIDO (Fast IDentity Online) 提出,該正式API 規格,透過瀏覽器的支援,可透過 Public Key Cryptography 機制註冊並認證使用者,取代傳統密碼形式的認證方式。

最傳統的身份認證機制是用密碼,但因為密碼太多了,又因為資安政策需要常常修改密碼,正常人應該已經沒辦法記得很清楚,哪個網站是用哪一個密碼。

WebAuthn 是  FIDO2 framework 的部分,透過 server, browser, authenticator 的搭配,提供一種 passwordless 無密碼的認證方式。

Demo

webauthn.io 是一個單純的網頁 demo。demo 時分兩個步驟,Register 與 Authenticate。

Register 就是註冊帳號,使用者向 browser 要求要註冊一個新帳號,browser 透過 authenticator 的搭配輔助,產生一組 public key,然後完成註冊的步驟。 authenticator 可能是本機的指紋 scanner,也可以是 USB key,或是目前最容易取得的每一個人的手機,並搭配手機的 FaceID 或是指紋 scanner 輔助做身份認證。

註冊完成後,會取得註冊的 id,然後在透過認證 API,進行使用者身份認證。

Javascript demo

註冊

// should generate from server
const challenge = new Uint8Array(32);
window.crypto.getRandomValues(challenge);

const userID = '5d157c55-ea8b-45ca-870a-da877a9c9916';

const utf8EncodeText = new TextEncoder();
const id = utf8EncodeText.encode(userID);

const publicKeyCredentialCreationOptions = {
    challenge,
    rp: {
        name: "Maxkit RP",
        // 這邊只是 demo,沒有放到某個 https 網站測試,如果是網站,就要改成該網站的 domain name
        //id: "maxkit.com.tw",
    },
    user: {
        id,
        name: "test",
        displayName: "Test",
    },
    pubKeyCredParams: [{alg: -7, type: "public-key"}],
    authenticatorSelection: {
        authenticatorAttachment: "cross-platform",
    },
    timeout: 60000,
    attestation: "direct"
};

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

把上面這一部分的 js code 直接貼到 chrome console 執行。主要就是透過 navigator.credentials.create 註冊產生 public key

challenge 欄位是由 server 產生,最終會由 authenticator 以私鑰簽章後,在 credential response 裡面傳回來,讓 server 能夠檢查確認 authenticator

rp.id 應該要填網域 domain name,這邊只是本機測試,不填寫

authenticatorAttachment: "cross-platform" 的意思是,可搭配使用外部認證工具,例如在 NB 瀏覽認證網頁時,可使用手機作為認證工具進行認證。

在 NB 提示認證時,會看到這個畫面

這時候,就用自己的手機相機,拍 QR code,手機上會出現以下畫面

確認後,會在手機的密碼部分,儲存一個專屬的密碼。iOS 手機選單在 "設定 -> 密碼"

在 console 列印 credential 的內容

// print credential
console.log(credential)
PublicKeyCredential {rawId: ArrayBuffer(20), response: AuthenticatorAttestationResponse, authenticatorAttachment: 'cross-platform', id: 'ku22CGGrZdYlkr9cCXL4IWtyYLc', type: 'public-key'}

正常狀況應該把 credential 傳送到 server,檢查並 decode 得到使用者的 id, public key。這邊僅是測試 demo,不做處理。

response.attestationObject 裡面有 public key 跟其他 metadata,是用 CBOR Concise Binary Object Representation 編碼後的 binary data。

認證

以下是認證的 demo code,從剛剛的 credential.id 欄位取得 id,通常應該是儲存在 server,從 server 傳到網頁得到這個資料

const credentialIdStr = credential.id;
const credentialId = Uint8Array.from(window.atob(credentialIdStr), c=>c.charCodeAt(0));

const challenge2 = new Uint8Array(32);
window.crypto.getRandomValues(challenge2);

const publicKeyCredentialRequestOptions = {
    challenge: challenge2,
    //rpId: "maxkit.com.tw",
    allowCredentials: [{
        id: credentialId, // from registration
        type: 'public-key',
        transports: ['usb', 'ble', 'nfc', 'hybrid'],
    }],
    timeout: 60000,
}

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

transports 是可以認證的方式

網頁同樣會出現 QR code,等待 authenticator,這邊我們用剛剛註冊的手機掃描

手機掃描時,會出現一個提示

點"使用通行密鑰登入"

繼續透過 FaceID 認證,認證完成,同樣可取得 response

console.log(assertion)

CTAP

Client-to-Authenticator Protocol,這是在瀏覽器打開註冊/認證網頁後,authnticator 跟瀏覽器之間傳輸的協定,也就是 WebAuthn API 跟外部硬體認證機制溝通的規格

References

WebAuthn guide

什么是WebAuthn:在Web上使用Touch ID和Windows Hello登录_webauthn 网站登录-CSDN博客

你可能不知道的WebAuthN(FIDO) | 又LAG隨性筆記

初探 WebAuthn UAF 的無密碼 Passwordless 登入技術 – 要改的地方太多了,那就改天吧

一起來了解 Web Authentication

CredentialsContainer - Web APIs | MDN