前言
點解會寫呢篇呢,因為我見cloudflare出yubikey coupon,查咗下資料發覺不經不覺webauthn已經支援到usernameless了,意味你只需要條key登記係賬號下面,就可以免打任何資料,Boom,登入咗。呢篇會儘量簡單(真係好簡單)啲講成件嘢點work,安全性,以及以最方便嘅usernameless(resident keys)嘅實作。
而WebAuthn基於FIDO系列協議,支援FIDO2嘅包括Yubikey都可以使用。但FIDO唔一定得硬件,軟件都可以的(只要照標準有keystore就得),所以Passkey其實都得。IOS有:
當然谷爹爹早就有了:
Passkey其實係on device 嘅fido2/webauthn,可以方便地直接進行passwordless/usernameless嘅登入,而不需要真實嘅硬件(你電話就可以直接auth)。
(蘋果唔使問,只要最新版本一定兼容,而且sync with icloud keychain)
Desktop嘅話,either Chrome or Safari都一樣支援嘅,唔使擔心。我實測咗Google
Crhome 同 Safari 嘅 passkey,Android/iOS上面測試Passkey影片係最後面。當然因為始有平台gap,所以都有試買yubikey。
Yubikey真係好用,真係建議大家買個試試。
如果你唔知揀邊個,揀下面呢兩個其中一個,兩個都喺支援最多protocol嘅,實無死。而呢兩個之間唯一分別只係一個係Typec一個係USB-A,但兩者都有NFC,手機可以直接拍。
歸類
首先講一講,一個標準webauthn嘅溝通流程。首先webauthn粗暴啲講,主要分成三種:
- 2FA 登入(需要帳號密碼)
- 免密碼登入(只需要帳號)
- 免用户名稱登入(有key就登入到)
而當中2FA登入本身係算OTP 2FA延伸,純粹加強你賬户嘅安全。而免密碼登入和免用户登入實際上係由驗證器/Authenticator(係呢度通常係yubikey 或者 ios嘅keychain之類) issue一個非對稱加密嘅組合。
一組非對稱加密嘅keys分別由private key同public key組成,由private key加密嘅內容只可以用 public key解密,反過來講用public key加密嘅只可以用private key解密。所以,只要首次溝通嘅時候,A預先把一組public key send俾B,咁之後就可以確保B收到並能正確用public key解密嘅文件,係的確來自正確嘅A先生(不考慮private key外泄然後被盜用情況嘅話)。
亦因為其不對稱性,所以廣泛用於現代電腦極多方面,常見嘅有HTTPS嘅TLS加密,郵件簽署嘅OpenPGP,遙控連線嘅SSH等,都有運用非對稱加密去進行加密,並保障連線溝通嘅安全。
而2和3嘅免密碼同免埋用户名稱登入嘅最大分別係,前者只含public key交換嘅部分,只能確保該request來自對應嘅驗證器。而在後者免用户名稱嘅當中規範咗套嘢叫discoverable cert / resisdent key,溝通過程會可以保留用户嘅唯一ID之類unique metadata係驗證器上面(Yubikey 5代只有25個slots儲存,ios/chrome就……cloud囉),可以sign埋呢user unique ID一齊使得可以消滅埋username。
流程&實作
Cisco有呢個explain咗Resident Key(usernameless/無用户名登入)嘅Registation,基本上就喺上面嘅嘢。而係實作上,大嘅部分分為兩大部分,分別係註冊條keys,同埋登入時候嘅驗證。
WebAuthn APi本身而家主流瀏覽器都支援了,只係原始嘅api比較多tricky嘢,所以都會pack性libary用,你可以根據你用嘅language search一下就有。而我下面呢個例子係基於simplewebauthn原本non-resident / 冇密碼登入 /passwordlress改出來的,
首先註冊嘅流程會係:
- Client/驗證器嘅呢邊發起請求,request 包括challenge在內嘅options params。
- Server 傳回,並附帶伺服器已經儲存咗嘅keys提醒用户唔好重複創建(if any)。
- client 獲得base option + challenge 之後,會問用户unlock keystore,驗證完ok之後,加入discoverable cert(加入user.id、user.displayname、等metadata)用private key sign完打包,向server 請求。
- server接收到之後,驗證完整性(包括challenge是否正確、origin是否一樣等)之後,用 user_id作key,把public key和user嘅metadata 儲存起來,並回傳。
- client收到正確嘅嘅singal後,顯示成功嘅提示。
跟住login/auth 嘅流程會係:
- client/驗證器問server 拎 params(包括challenge),server回傳。
- client拎到之後會問用户unlock keystore + 揀相應嘅cert/user_id,無問題嘅話就會sign 然後回傳 server。
- server收到之後檢查一輪,確認的確係呢個user嘅話,就回傳對應用户嘅登入所需嘅token(例如jwt之類)
- Client收到,go auth。
代碼
因為我反正都要寫,所以都另外根據官方嘅example,改咗做resident嘅,大家可以參考睇睇:
另外我本身係firebase為主,所以亦整咗個firebase functions嘅server side 同 angualr用嘅class:
參考
跟住官網有一堆唔同language嘅libary,自己可以慢慢睇。