[WebAuthn] (1)概論及用戶(hù)注冊(cè)流程
Web authentication API, aka WebAuthn, 是Web平臺(tái)用于提供基于公鑰密碼學(xué)的無(wú)密碼鑒權(quán)或安全的雙(多)因子認(rèn)證的API。WebAuthn并使用瀏覽器的憑據(jù)管理API存儲(chǔ)密鑰對(duì)*。
?在公鑰密碼學(xué)中,一組對(duì)應(yīng)的公鑰和私鑰稱(chēng)為一個(gè)密鑰對(duì)。公鑰通??梢暂^輕松得從私鑰計(jì)算得出,而私鑰(應(yīng)當(dāng))無(wú)法在有意義的時(shí)間*內(nèi)從公鑰得出。用私鑰編碼(通常稱(chēng)為簽名)的信息可以用公鑰解碼(通常稱(chēng)為驗(yàn)證),用公鑰編碼(通常稱(chēng)為加密)的信息可以(只能)用私鑰解碼(通常稱(chēng)為解密)。
現(xiàn)代密碼學(xué)的一個(gè)常識(shí)是沒(méi)有任何密碼系統(tǒng)是絕對(duì)安全的,這一方面是在說(shuō)漏洞永遠(yuǎn)可能存在,另一方面是關(guān)于時(shí)間,因?yàn)橥ㄟ^(guò)窮舉計(jì)算進(jìn)行的破解總是可行的。衡量一個(gè)密碼系統(tǒng)強(qiáng)度的關(guān)鍵屬性是時(shí)間:即“平均來(lái)說(shuō)”使用該密碼系統(tǒng)進(jìn)行的一次通信在多長(zhǎng)時(shí)間之內(nèi)是安全的,以及該密碼系統(tǒng)本身在多長(zhǎng)時(shí)間之內(nèi)是安全的。在決定使用何種密碼系統(tǒng)和密鑰長(zhǎng)度時(shí),時(shí)間是需要考慮的關(guān)鍵因素之一。但是即便如此,攻擊者依然可能在顯著更短的時(shí)間內(nèi)破譯通信,雖然這種可能性能夠被顯著降低。
這也意味著整個(gè)現(xiàn)代密碼學(xué)幾乎完全建立在P不等于NP的假設(shè)之上:因?yàn)橹烂荑€時(shí)的解密應(yīng)當(dāng)可以快速完成,這就將一個(gè)密碼系統(tǒng)所依賴(lài)的困難問(wèn)題限制在了NP問(wèn)題的范圍之內(nèi)。如果真相是P=NP,那么目前所有的密碼系統(tǒng)都會(huì)是脆弱的,并且將來(lái)也幾乎不可能再有新的強(qiáng)度足夠高的密碼系統(tǒng)。
另外,還有一點(diǎn)需要注意的是,隨著算力的不斷提高和破譯技術(shù)的改進(jìn),一個(gè)在目前看來(lái)能夠提供數(shù)十年安全時(shí)間的密碼系統(tǒng),可能在更近的未來(lái)被更快攻破,攻擊者可以記錄現(xiàn)在(和過(guò)去)的密文以供未來(lái)破譯,因此衡量安全性不僅應(yīng)當(dāng)考慮當(dāng)下的水平,還需要估計(jì)未來(lái)的情況,然而對(duì)未來(lái)的估計(jì)永遠(yuǎn)是錯(cuò)誤的,所以,

然后是免責(zé)聲明:現(xiàn)代密碼學(xué)的另一個(gè)常識(shí)是不要?jiǎng)?chuàng)建你自己的密碼系統(tǒng)。一個(gè)密碼系統(tǒng)的安全性不僅需要數(shù)學(xué)證明,更需要事實(shí)證明。一個(gè)密碼系統(tǒng)需要經(jīng)受住嚴(yán)格挑戰(zhàn)才有足夠的證據(jù)信服其安全,而一個(gè)新系統(tǒng),即使它的作者是全世界最專(zhuān)業(yè)的密碼學(xué)家,也缺乏這種事實(shí)證明。因此,不要?jiǎng)?chuàng)建你自己的密碼系統(tǒng),不要將未經(jīng)驗(yàn)證的密碼系統(tǒng)用于演示以外的用途,這包括在下文中演示的技術(shù)。
這也意味著,新的技術(shù)意味著新的風(fēng)險(xiǎn)和不確定性,以及更少的現(xiàn)實(shí)驗(yàn)證。當(dāng)然新技術(shù)可能提供更好的結(jié)果,但新技術(shù)在應(yīng)用之前首選需要得到充分驗(yàn)證。
另外,讓一個(gè)密碼系統(tǒng)得到充分驗(yàn)證的最好方式之一是公開(kāi)其實(shí)現(xiàn),這種“透明式安全”的設(shè)計(jì)理念也是柯克霍夫原則的體現(xiàn),但這并不意味著只要是公開(kāi)實(shí)現(xiàn)的系統(tǒng)就一定能得到充分的公開(kāi)驗(yàn)證。當(dāng)然,我不會(huì)批評(píng)“不使用‘透明式安全’設(shè)計(jì)的系統(tǒng)本質(zhì)上不可靠”的觀(guān)點(diǎn),但是基于現(xiàn)實(shí)考慮,并不是說(shuō)不采用“透明式安全”就是不對(duì)的,或者只要采用了“透明式安全”的系統(tǒng)就一定是安全的。對(duì)于后者我們已經(jīng)見(jiàn)過(guò)足夠多的反例了。畢竟,“沒(méi)有系統(tǒng)本質(zhì)上是可靠的”。
------吟唱完畢------
雖然據(jù)caniuse和mdn顯示,目前所有的主流瀏覽器的正式版本都已對(duì)webauthn提供了支持,但是,WebAuthn目前依然應(yīng)當(dāng)被認(rèn)為是尚不完善,試驗(yàn)性,未經(jīng)充分驗(yàn)證的。實(shí)際上,它的用戶(hù)界面也還沒(méi)有那么完整,距離大規(guī)模商用可能還需要一段時(shí)間。
不過(guò),無(wú)密碼身份認(rèn)證(實(shí)際上更準(zhǔn)確的說(shuō)法是無(wú)口令身份認(rèn)證)一直有著不小的熱度,而隨著各平臺(tái)上安全芯片的普及,無(wú)密碼身份認(rèn)證的實(shí)施正在變得越來(lái)越簡(jiǎn)單,作為Web端無(wú)密碼認(rèn)證基礎(chǔ)的WebAuthn在將來(lái)會(huì)成為其中不可或缺的一環(huán)。雖然對(duì)于開(kāi)發(fā)者來(lái)說(shuō)未來(lái)會(huì)有完善的認(rèn)證庫(kù)供使用,但是了解其中的流程和原理依然是必要的。
另一方面,即使對(duì)于普通用戶(hù)來(lái)說(shuō),了解正在快速普及的無(wú)密碼認(rèn)證背后的機(jī)制和理念,對(duì)用戶(hù)個(gè)人的信息安全同樣是必要的。
注冊(cè)流程
作為一款基于公鑰密碼學(xué)的API,WebAuthn的注冊(cè)過(guò)程即創(chuàng)建及認(rèn)證密鑰對(duì)的過(guò)程,在這個(gè)過(guò)程中我們將使用`navigator.credentials`作為密鑰對(duì)的憑據(jù)存儲(chǔ)。在大多數(shù)瀏覽器上,這項(xiàng)功能只允許在安全環(huán)境中使用,不過(guò),雖然具體細(xì)節(jié)有所不同,file://和localhost通常會(huì)歸為安全環(huán)境以方便開(kāi)發(fā)者(不然你就需要在本地部署自簽名的https服務(wù)器來(lái)測(cè)試僅安全環(huán)境可用的功能了)。下文中我們將在本地啟動(dòng)一個(gè)web服務(wù)器用于測(cè)試。為了方便大家測(cè)試,這里不會(huì)使用任何前端框架。因?yàn)锽站專(zhuān)欄對(duì)嵌入代碼不太友好,下文中的代碼將主要以截圖展示,最終的示例代碼會(huì)在整個(gè)系列完成后一并給出。
雖然在實(shí)際應(yīng)用中不多見(jiàn),但WebAuthn的全部流程實(shí)際上都可以在瀏覽器中完成,因此除非特殊需要,本文的示例將盡量保持在瀏覽器中。注意,這是因?yàn)楸疚耐耆鲇谘菔灸康模悄忝鞔_知道這樣做的理由,對(duì)公鑰的認(rèn)證通常應(yīng)當(dāng)由“依賴(lài)方”,一般來(lái)說(shuō),也就是在服務(wù)器中進(jìn)行。

在chrome中你會(huì)看到如下提示:

在Chrome中,用戶(hù)可以換用實(shí)體安全密鑰(智能卡)或登錄了同一賬號(hào)的帶有安全芯片的Android設(shè)備,也可以選擇將密鑰存儲(chǔ)在當(dāng)前設(shè)備中。用戶(hù)可以在密碼管理器->管理通行密鑰中看到所有此設(shè)備上的密鑰。
在Chrome Android中,本機(jī)存儲(chǔ)的密鑰在密碼管理器中與密碼一同列出(不包括其他設(shè)備使用此設(shè)備作為安全密鑰的情況)。
Android設(shè)備可以在Chrome Android的“隱私設(shè)置和安全性->將手機(jī)用作安全密鑰”界面重置用于其他設(shè)備的安全密鑰。

這是在MacOS Safari中彈出的提示

目前為止Safari(或者說(shuō)MacOS和iOS)尚未提供可用的密鑰管理界面。
注冊(cè)信息與密鑰的認(rèn)證
我們需要驗(yàn)證返回的key并獲取公鑰,與驗(yàn)證和公鑰相關(guān)信息在`key.response`中,其中`key.response.clientDataJSON`是一些json格式的信息,然后以u(píng)tf-8編碼保存在一個(gè)ArrayBuffer中,其中包含明文的challenge和一些信息,而`key.response.attestationObject`是CBOR編碼的驗(yàn)證信息,此處我們引入一個(gè)CBOR編解碼庫(kù)用于編解碼,這個(gè)編解碼庫(kù)托管于github:paroga/cbor-js。

我們首先需要驗(yàn)證此處的challenge是否與傳入的challenge相同,如果不同,那么證明注冊(cè)流程出現(xiàn)了問(wèn)題,應(yīng)當(dāng)終止這次注冊(cè)。
然后,我們需要從attestation中獲取信息。attestation的信息由兩部分組成,一部分是存儲(chǔ)于attestationObject.authData的authenticator data,這里主要是公鑰,另一部分是attestationObject.attStmt的attestation statement,也就是用于驗(yàn)證的信息,對(duì)于本文和多數(shù)情況來(lái)說(shuō)其中有用的信息是challenge的簽名。

AuthData具體格式在https://w3c.github.io/webauthn/#authenticator-data,公鑰在attestedCredentialData,它開(kāi)始于authData的第37字節(jié),以16字節(jié)(即第37至第52字節(jié))的AAGUID開(kāi)頭,緊接著是2字節(jié)(第53,54字節(jié))的憑據(jù)id長(zhǎng)度,然后是相當(dāng)于此長(zhǎng)度L(第55至第55+L-1字節(jié))的憑據(jù)id,然后是可變長(zhǎng)度的COSE_KEY格式的公鑰(開(kāi)始于第55+L字節(jié)),所以接下來(lái)我們要取出這個(gè)公鑰。

對(duì)于ecdsa算法,cose編碼的公鑰會(huì)類(lèi)似下圖所示,其中`3:-7`指示公鑰所使用的算法為ES256,而-1,-2,-3分別指示公鑰使用的曲線(xiàn)和坐標(biāo):

接下來(lái)我們使用這個(gè)公鑰來(lái)驗(yàn)證attStmt中的簽名。attStmt的具體格式被attestationObject.fmt規(guī)定。受限于可用庫(kù)和篇幅限制,接下來(lái)的代碼將僅限于ES256算法簽名的packed驗(yàn)證格式。下文將使用Crypto.subtle驗(yàn)證簽名,該API同時(shí)也提供了其他的算法組合。一般來(lái)說(shuō),ES256會(huì)使用COSE格式中編號(hào)為1的p-256曲線(xiàn)。在packed格式中,簽名算法由attStmt.alg標(biāo)示,簽名存儲(chǔ)在attStmt.sig,attStmt.x5c指示關(guān)于驗(yàn)證器的證書(shū)鏈,或者說(shuō)它通常用來(lái)指示實(shí)體安全密鑰或安全芯片來(lái)自可被驗(yàn)證的廠(chǎng)商。如果驗(yàn)證器是“自認(rèn)證”的,那么這個(gè)參數(shù)將不會(huì)提供。鑒于瀏覽器內(nèi)建的密鑰存儲(chǔ)功能通常是“自認(rèn)證”的,下文將僅限于驗(yàn)證自認(rèn)證簽名。
Crypto.subtle使用的ES256證書(shū)的原始二進(jìn)制格式為<0x04 x y>,這里同樣也可以轉(zhuǎn)換為jwk或其他格式導(dǎo)入,但是raw格式導(dǎo)入比較方便。
另外,WebAuthn中的ECDSA簽名使用ASN.1格式,它的具體二進(jìn)制格式為<0x30 b1 0x02 b2 r 0x02 b3 s>,而Web Crypto API使用的簽名格式為P1363,二進(jìn)制格式為r|s,在驗(yàn)證簽名時(shí)需要轉(zhuǎn)換。

這樣我們就得到了證書(shū)和p1363格式的簽名,但是還需要另一個(gè)關(guān)鍵參數(shù),就是簽名的“原文”。https://w3c.github.io/webauthn/#sctn-packed-attestation約定了packed attestation生成簽名的程序。需要注意如果attestation format不是packed,此處的程序會(huì)有不同。
我們看到最終被簽名的原文包含了challenge,依賴(lài)方,用戶(hù)以及密鑰本身相關(guān)的信息,因此這個(gè)簽名可以證明此次注冊(cè)請(qǐng)求,請(qǐng)求雙方以及請(qǐng)求生成的密鑰間的對(duì)應(yīng)關(guān)系。

至此注冊(cè)流程完畢。用戶(hù)已經(jīng)將密鑰對(duì)存儲(chǔ)于憑據(jù)存儲(chǔ)中,而依賴(lài)方得到了可以用于驗(yàn)證用戶(hù)身份的公鑰。接下來(lái),我們將演示用戶(hù)與依賴(lài)方之間如何利用密鑰對(duì)確認(rèn)用戶(hù)身份。
有余力的讀者可以自行查閱其他認(rèn)證格式和認(rèn)證方式的資料,并嘗試使用安全芯片或?qū)嶓w安全密鑰代替瀏覽器憑據(jù)存儲(chǔ)完成注冊(cè)流程。