存在瀏覽器裡的密碼使用起來是非常方便的,大家應該也都習慣了密碼自動填寫的便利,但是這些密碼是如何存儲的,以及瀏覽器批量存儲這些密碼真的安全嗎,這都是需要進一步考量的,正好之前瞭解過主流的兩類瀏覽器(及使用這兩類瀏覽器內核)的密碼管理協議,在這裡做一下分享。
本篇文章可能有點硬核,我儘量把它寫的更偏科普一些。為了不引起歧義,下文中我們把用戶存儲的網站和賬戶密碼稱作"口令"。
1.firfox記住密碼的原理
Firefox利用了本地客戶端、密碼服務器和一個安全密碼查詢協議和一個密碼驗證協議來實現整個功能。
簡單而言,客戶端存儲賬戶信息和密碼(該密碼不會發送給密碼服務器,否則會使服務器得到所存儲密鑰加密值的明文),客戶端利用密碼驗證協議證明自己為密碼持有者,為自己產生會話令牌,服務器會驗證該令牌。服務器會將口令分為A類和B類:
- A類:即使用戶忘記了密碼,也可以通過證明對電子郵件地址的控制權(ID)並重置帳戶來恢復分配給此類的口令。A類主密鑰存儲在服務器上,用於幫助用戶找回丟失口令,不同數據類型的單個加密密鑰均派生自 kA。
- B類:如果忘記密碼,則無法恢復此類中的數據。即使擁有用戶的IdP 無法讀取它。密鑰服務器只存儲 wrap(kB),並且永遠不會看到 kB 本身。客戶端(瀏覽器)使用從用戶密碼派生的密鑰(這一步使用了 Key Stretching,得到B類密鑰的派生密鑰)來解密 wrap(kB),獲取真正的 kB。
下面將會非常詳細地對整個流程進行分析,如果確實看不懂可以調到第二部分chrome的密碼記錄分析:
1.1 創建口令管理賬戶並分配秘鑰
當用戶在客戶端輸入email和password後,運行100輪派生函數PBKDF2,使用用戶的郵件地址作為salt生成quickStretchedPW,然後將其作為HKDF的參數,得到authPW,在此過程中得到了密碼強度足夠(主要是保證隨機性)的派生密鑰authPW。最後,將郵件地址和對應的authPW發送給服務器。
服務器得到密鑰後,會對該密鑰進一步擴展,同時生成哈希表存儲索引值便於遍歷。
1.2 口令查詢/回話建立
口令查詢包括獲得會話令牌和創立會話
我們先來談談獲得會話令牌,主要步驟如下圖所示:
要將客戶端連接到服務器,需要使用登錄協議將電子郵件+密碼對轉換為sessionToken。
該協議首先在客戶端將密碼和電子郵件地址輸入 1000 輪 PBKDF2 以獲得quickStretchedPW,將 quickStretchedPW 輸入 HKDF 以獲得authPW,然後將電子郵件 + authPW 發送到服務器的端點。
服務器使用電子郵件地址查找數據庫行,提取authSalt,執行與帳戶創建期間相同的拉伸,以獲取bigStretchedPW,驗證哈希值得到verifyHash,然後將verifyHash與存儲的值進行比較。如果它們匹配,則客戶端已證明知道密碼,服務器將創建一個新會話。服務器將新生成的會話令牌及其帳戶標識符 (uid) 返回給客戶端。
值得注意的是,在本協議中,服務器支持每個帳戶同時存在多個會話(通常每個客戶端設備一個會話,可能還有其他用於帳戶管理門戶的會話)。這代表sessionToken 永久存在(直到被密碼更改或顯式吊銷命令撤銷),並且可以無限次使用。
對於已驗證電子郵件地址的帳戶(具有會話令牌sessionToken)的客戶端可以使用api獲取經由簽名的瀏覽器 ID/用戶配置證書。然後,可以使用此證書生成已簽名的瀏覽器 ID assertions,傳遞到服務器的./certificate/sign。過程如下圖所示:
1.3 更改/找回密碼
當用戶更改其密碼(即他們仍然知道舊密碼)時,或當用戶忘記密碼時,需要重置賬戶和密碼。下面將分別討論兩者情況:
當需要利用舊密碼重置密碼時,需要完成下圖流程:
其主要協議流程為驗證用戶身份,之後用戶使用舊密碼將存儲在服務器的wrap(kb)解密,然後使用從新密碼派生的新密碼重新包裝kb生成新的wrap(kb)。
對於找回密碼,用戶首先需要訪問fxa-content-server(即服務器)上的頁面,服務器向用戶發送一封帶有恢復代碼的電子郵件,然後在用戶單擊該電子郵件中的鏈接,利用存儲的郵件地址來驗證自己的身份,從而重置密碼。
具體而言,當用戶申請重置密碼,服務器將相應的帳戶標記為“待恢復”,為該帳戶分配一個隨機密碼ForgotToken,創建一個恢復代碼,並向用戶發送電子郵件,其中包含URL(指向fxa-content-server)、passwordForgotToken和恢復代碼作為query-args。
當用戶單擊鏈接時,加載的 fxa 內容服務器頁面會將令牌和代碼提交到 API。當服務器看到匹配的令牌和代碼時,它會分配一個backtoken並將其返回到提交它們的客戶端(頁面)。然後,客戶端可以創建新的密碼並重復創建賬戶時的密碼創建流程,如下圖所示:
值得注意的是,一旦重置密碼,將會丟失所有B類數據,這避免盜取郵箱的攻擊者獲得用戶的所有口令,用戶只能得到存儲的A類數據。
這個設置是必要的,如果B類和A類不分開存儲,那麼同時也會有攻擊服務器的攻擊者,一旦得到服務器控制權,就可以解密A類,但是無法解密B類口令。
1.4 安全性分析
本協議使用基於 HKDF 的派生密碼用於保護本協議中請求的內容。HKDF 用於創建多個與消息長度相等的隨機字節,然後將這些字節與明文進行 XOR 運算以生成密文。然後從密文計算 HMAC,以保護消息的完整性。之所以使用該方案是因為HKDF首先可證明計算安全,其次易於指定且易於實現(只需要實現HMAC和異或操作)。
那麼盜號🐶該如何盜號呢?首先,攻擊者可以暴力枚舉kb,但這意味著他們必須對要測試的每個猜測的密碼進行1000輪PBKDF和其他操作,這是計算上困難的,用自己家的電腦得算上幾萬年。
其次,盜號🐶可以通過web漏洞如果控制了服務器(如利用中國蟻劍等webshell工具獲得服務器webshell),獲得了文件讀寫權限,那麼攻擊者也無法解密wrap(kB)。此時A類數據將會被竊取。
第三,即使盜號🐶利用盜取的郵箱重置用戶密碼,也無法訪問B類數據(關於ab類,在文章開頭我們定義了,它代表數據的安全級別),B類數據會丟失。此時A類數據將會被竊取。
2.Chrome瀏覽器的記住密碼
Chrome密碼管理器採取了與firfox完全不同的安全策略,其將密碼和加密過的數據均存儲在用戶本地,以windows為例,Chrome通常將使用windows API CryptProtectData 直接將加密後的口令存儲在AppData\Local\Google\Chrome\User Data\Default\ Login Data,將密碼存儲在AppData\Local\Google\Chrome\User Data\ Local State。
Chromee的立場是主密碼提供了一種虛假的安全感,保護敏感數據的最可行保護方式是要取決於系統的整體安全性。所有,為了執行加密(在Windows操作系統上),Chrome使用了Windows提供的CryptProtectData API,該API允許已經用於加密的Windows用戶賬戶去解密已加密的口令。所以也可以理解為主密碼就是Windows賬戶密碼,即只要登錄了Windows賬號,Chrome就可以解密口令,並導入密碼管理器中。
就是說,如果攻擊者通過腳本(對了,就是我之前那篇文章提到的偽入庫的腳本)獲得了本地電腦的賬號口令,那麼你所有的密碼都會被帶走。換言之,chrome不需要像firfox那樣對你的秘鑰安全負責,你對自己的秘鑰直接負責。
我自己寫了個腳本測視了一下我的chrome,果然直接跑出了密碼,確實有點離譜:
所以最後結論就是大家還是要謹慎對待記住密碼這個功能,特別是使用chrome瀏覽器同時還經常偽入庫是盒友,不然就只能: