7.42 密碼重設流程:與登入平行的另一道入口
本章處理的是「不知道密碼的人證明自己是帳號主人」這條路徑的設計。
7.31 認證方式選型 與 7.39 使用者持有型憑證 都得出過同一個結論——實際的安全上限由回復路徑決定,而不是由主路徑用了什麼機制決定。這一章展開那條路徑本身。
本章涵蓋與不涵蓋
本章聚焦重設流程的設計。帳號已經被接管之後的處置與核身在 7.41 單一帳號被接管,那一章用本章的結論當前提。大量帳號的強制重設(外洩之後的分層範圍、期限與容量攤平)在 7.37 密碼外洩之後。密碼存成什麼樣、參數怎麼定在 7.30 使用者密碼儲存。委派身分的系統把主路徑交了出去而回復路徑常常留在自己這邊,那個組合的判讀在 7.31。
判讀軸:這是入口,不是例外處理
重設流程在多數系統的心智模型裡是「登入失敗之後的補救」,而在攻擊者的模型裡它是與登入平行的第二道入口——而且是比較好走的那一道,因為它不需要知道密碼。
這個定位差異解釋了為什麼投入通常不對稱。登入端點會做速率限制、會有異常判讀、會上第二因子;重設流程常常只有一封信。而整個帳號的安全上限由兩道入口裡較弱的那一道決定,於是主路徑上的所有投資都被這一道封頂。
判準因此只有一句:這條路徑要求的證據,攻擊者拿不拿得到。下面幾節逐一套用在常見的做法上。
常見證據各自的問題
寄到已綁定的信箱是主流做法,而它把這個帳號的安全水準交給了那個信箱帳號。使用者的信箱多半仍由一組密碼守著,於是這條路徑的實際強度等於那組密碼——而它可能與被重設的帳號用的是同一組。這一項不是不能用,是要知道它把上限訂在哪裡:信箱被接管時,所有用信箱回復的服務一起淪陷。
簡訊驗證碼多一層實體持有,而它的失效形態很具體:號碼被移轉到攻擊者的門號(社交工程電信商)、或簡訊在鎖定畫面上直接可讀。它比信箱好一些而不是好很多。
預設問題與個人資料在今天多數情境下不構成證據。母親的姓、第一輛車、出生地這一類的值在別處外洩過、或本來就查得到;使用者為了記得住而填真實答案,於是它們與公開資訊高度重疊。填假答案的使用者則會忘記自己填了什麼,這條路徑因此又回到客服。
已註冊的第二個裝置或憑證是強度最高的一種,因為它要求攻擊者實體持有。代價是使用者要先註冊第二支,而消費端的註冊率低——這條取捨見 7.39 掉了之後。
人工核身的強度由核身項目決定而非由「有人在看」決定,判準與 7.41 核身要求什麼 相同:要問接管之前的事、要問攻擊者取得不了的資訊。
重設連結本身要滿足什麼
連結是一段可以直接使用的憑證,它在被點開之前的每一個環節都可能外洩。
一次性:用過即失效。沒有這一項時,信箱裡那封信永遠是一把可用的鑰匙,而舊信件的保存期限通常以年計。
時效短:有效期以分鐘計而非以天計。使用者當下就在等這封信,長效期換到的方便有限,而它讓被動外洩(共用電腦、被同步到別的裝置、企業信箱的離職交接)的窗口變長。
不可猜:用密碼學安全的隨機來源產生、長度足以抵抗大量嘗試。這一項聽起來理所當然,而實作上出過的形態是拿使用者識別值加時間戳做雜湊——那可以被離線構造。
不留在紀錄裡:權杖放在網址的查詢字串時,它會進入伺服器的存取日誌、瀏覽器歷史、以及使用者從該頁點出去時送出的來源標頭。把它放在網址的片段部分或改用表單送出可以避開多數這些落點,而最簡單的替代是讓使用者輸入一組短碼而非點連結。
綁定用途:這段權杖只能用來重設密碼,不能拿來當一般的登入憑證。混用的系統會讓一封重設信變成一次完整的會話。
重設完成之後還缺什麼
改掉密碼不會讓已經發出的會話失效,也不會移除攻擊者留下的持久存取。這兩件事在重設流程裡都要接上,否則使用者拿回的是一個仍然開著的帳號。
撤銷全部會話是必要的收尾。撤銷得掉的前提是會話狀態放在自己查得到的地方,見 Session 處理 與 7.37 撤銷會話與重設密碼是兩個動作。
通知使用者這件事剛剛發生,並在通知裡給一條「這不是我做的」的路徑。這封信要寄到變更前的聯絡方式——寄到變更後的話,收到的是攻擊者。
檢視持久存取:長效權杖、應用程式密碼、第三方授權、回復管道的設定。這一項在自助流程裡通常做不到逐項還原,最小做法是把它們列出來給使用者看,判讀見 7.41 攻擊者留下什麼。
這條流程會洩漏什麼
重設流程要輸入帳號識別值,於是它天然是一個帳號存不存在的查詢介面。回應「這個信箱沒有註冊」等於確認了另一個信箱有註冊,而攻擊者拿外洩清單來跑一遍就得到一份有效帳號名單。
處置是回應與時間都不因帳號存不存在而不同:一律回「若這個信箱有註冊,我們已經寄出信件」,而且兩種情況花的時間相近——只在帳號存在時才做雜湊或寄信的實作,會由回應時間洩漏同一件事,判讀見 timing attack。
這一項要與產品的可用性一起衡量。使用者打錯信箱時得不到明確的錯誤訊息,客服詢問會因此上升。多數面向消費者的服務接受這個成本,而內部工具或已知使用者集合的系統可以做不同的取捨——前提是這個取捨被決定過。
判讀流程
- 列出所有能重設密碼的路徑,含自助流程、客服流程、以及舊版行動應用可能留下的獨立端點。安全上限由最弱的那一條決定。
- 對每一條問「它要求的證據,攻擊者拿不拿得到」。答案是「拿得到」時,寫出這條路徑把上限訂在哪裡(那個信箱帳號、那個門號、那份外洩資料)。
- 檢查重設權杖的五項性質:一次性、時效、不可猜、不留在紀錄裡、綁定用途。
- 確認完成之後的三個收尾都接上了:撤銷全部會話、通知變更前的聯絡方式、列出持久存取。
- 決定帳號存在與否要不要保密,並讓回應內容與回應時間一致。這是一個取捨、要有人決定過。
- 對重設端點做速率限制,判準與登入端點相同——它是同一類的入口。
這個決定失敗時長什麼樣
一個團隊把登入做得很紮實:外洩密碼比對、異常判讀、第二因子、passkey 支援。重設流程沿用系統上線時的實作,因為它一直沒出過事。
那個實作是寄一封含連結的信,連結沒有時效、用過也不會失效,而且權杖放在網址的查詢字串裡。這幾件事單獨看都不嚴重——信寄到使用者自己的信箱、內容也只有他看得到。
出事的形態是這樣:某個使用者的舊筆電被轉手,瀏覽器歷史裡留著三年份的網址;或某個企業帳號的離職交接把整個信箱移交給接手的人,而信箱裡有一封兩年前的重設信。兩種情況下那把鑰匙都還能用,而系統這一側完全沒有異常——一次成功的密碼重設,走的是設計好的流程。
沒有人發現的原因是這條路徑從來不在檢視範圍內。安全審查看的是登入,滲透測試看的是登入,而重設流程被歸類成「使用者體驗的補救措施」。它的投入與它承擔的責任因此長期不成比例。
常見風險邊界
- 列得出登入端點做了哪些防護、列不出重設流程做了哪些時,帳號的上限落在後者,而它沒有被評估過。
- 重設連結沒有時效或不是一次性時,使用者信箱裡的每一封舊信都是一把還能用的鑰匙,而信件的保存期限以年計。
- 權杖放在網址的查詢字串時,它會落進伺服器日誌、瀏覽器歷史與來源標頭,而這三處的保存期限都不由自己控制。
- 重設完成之後沒有撤銷會話時,攻擊者在重設之前建立的那個會話仍然有效,使用者以為換了密碼就安全了。
- 變更通知寄到變更後的聯絡方式時,收到的是攻擊者,而真正的使用者不會知道。
- 自助流程與客服流程的核身強度不一致時,攻擊者走比較鬆的那一條,而兩條路徑的擁有者通常不是同一個團隊。
- 重設端點沒有速率限制時,它可以被拿來當帳號列舉工具,也可以被拿來對單一使用者的信箱發動洗版。
下一步路由
- 主路徑用什麼驗證使用者、以及回復路徑決定上限這個結論的來源:7.31 認證方式選型
- 帳號已經被接管之後的處置與核身:7.41 單一帳號被接管
- 大量帳號的強制重設、期限與容量攤平:7.37 密碼外洩之後
- 憑證掉了的補發路徑(同一個問題在持有型憑證上的形態):7.39 使用者持有型憑證
- 撤銷會話的能力由狀態放哪決定:Session 處理
- 回應時間洩漏資訊的機制:timing attack
- 密碼存成什麼樣、參數怎麼定:7.30 使用者密碼儲存