7.30 使用者密碼儲存:參數會過期的那一類原語
本章的責任是把使用者密碼該怎麼存這件事拆成可判讀的選型問題。它的特別之處是保護強度會隨時間自己下降——同一組設定放著不動,每一年都比前一年弱,所以這裡的產出除了演算法,還包括一個要定期重算的參數與一條升級路徑。
本章省了同模組其他章有的幾個段落——威脅範圍宣告、問題節點表、案例觸發參考——因為這個主題的判讀軸只有一條(計算成本),那套結構用不上。
本章涵蓋與不涵蓋
本章聚焦不可逆單向轉換用在使用者密碼上的選型判讀,是 7.28 密碼學原語選型 第四類原語的展開。認證流程本身(登入節奏、多因子、會話管理)屬 7.2 身分與授權邊界,內容校驗用的快速雜湊(checksum、檔案指紋)不在本章範圍——它與密碼儲存的判讀軸相反,那裡速度是優點。
先確認這一章適用
本章的所有判讀都建立在一個前提上:這個服務自己存使用者密碼。委派身分與 passkey 都能讓這個前提消失,但兩者都有把問題引回來的縫——使用者來源分散時委派多半留有密碼 fallback,passkey 需要帳號回復路徑,而回復路徑最後常常落回某種可重設的祕密。判準是 7.31 的兩問——那一段材料是不是人選出來、人記得住的,以及攻擊者拿到落地的那一份之後驗證猜測還需不需要再打這個服務。兩項都成立的那一段適用本章,不論主路徑用了什麼。
這個決定本身的完整取捨見 7.31 認證方式選型。
這一類原語的判讀軸
密碼儲存的判讀前提是資料已經被拿走——其他原語防的是攻擊者拿不到資料,這一類從一開始就假設拿到了。資料庫外洩之後,攻擊者手上有全部的雜湊值與 salt,唯一還在運作的保護是「逐一嘗試要花多久」。
攻擊者的總代價是「猜一次要付的代價」乘上「要猜幾次」,而本章的參數只買得到第一項。第二項由密碼本身的強度決定,而它是支配項——參數調高十倍換到的難度增量,遠小於擋掉外洩密碼清單、要求第二因子或改用 passkey 換到的。所以這一章的定位要先講清楚:它讓每一次猜測變貴,擋不住弱密碼。密碼本身已經在別處外洩過時連「猜」都不必——那一類攻擊見 credential stuffing,本章的參數對它完全無效。第二項的處置(登入節奏、外洩密碼比對、第二因子)走 7.2 終端使用者的登入節奏。
參數本身要在三項成本之間定:攻擊者猜一次要付的代價、正常使用者登入時要等的時間、以及伺服器承擔的運算與記憶體。三項用的是同一組參數,調高第一項就同時調高後兩項,所以這個選型的產出是一個區間而不是一個值。
這條軸還有一個其他原語沒有的性質:參數的有效性隨硬體變快而下降。金鑰位置選對了就一直對,密碼的 work factor 定好之後每年都比前一年弱。這決定了本章的交付包含一個複查節奏,而不只是一次決定。
攻擊者的硬體優勢從哪裡來
各種演算法的排序由一件事決定:它們針對攻擊者的哪一種優勢收費。知道這一點之後,「演算法選型」的排序讀起來是推論而非清單,遇到新演算法也判得出它落在哪裡。
雙方跑的是同一個計算,差別在硬體。防守方在通用 CPU 上一次算一個;攻擊者可以用 GPU(數千個簡單核心)或訂製晶片(把演算法直接做成電路)同時算幾萬個。這個差距的來源是平行度——單次計算需要的資源越少,複製幾萬份的成本就越低。抵抗的方向因此是讓「複製一份」變貴,而現有的做法在三個位置收費。
時間是最直觀的一項,PBKDF2 只有這一項。它的單次計算只需要極少的記憶體與很簡單的運算,於是一顆 GPU 能塞進去的平行實例數量幾乎不受限制。把迭代次數乘以十,防守方與攻擊者的成本同時乘以十,比例維持不變——這是它抗硬體能力最弱的結構原因,而非實作品質的問題。
記憶體改變的正是這個比例,而以此為設計目標的構造稱為記憶體硬化函式。每個平行實例都要獨佔一塊記憶體,於是平行度被晶片上的記憶體總量封頂;而記憶體在晶片面積上的成本遠高於計算單元,攻擊者要把平行度買回來付的是完全不同量級的錢。Argon2 的 m 與 scrypt 的 N 買的都是這一項,它是目前對訂製硬體最有效的收費位置。
記憶體的存取形態是第三項,也是 Argon2 分成三個變體的那條軸。讀取位置由資料本身決定時,攻擊者無法預先排程搬運,快取與預取一併失效;代價是存取形態會洩漏資訊給觀察得到快取行為的旁通道攻擊者。d 取資料相依(抗硬體強、有旁通道風險),i 取資料獨立(相反),id 在第一趟的前半用 i、其餘用 d——前段不洩漏存取形態、其餘取抗硬體,這是它成為預設變體的理由。
bcrypt 的固定內部狀態在這個框架裡有明確的位置:它有記憶體項,但小且不可調。約 4 KB 這個量級至今仍超過 GPU 每個核心可用的快取,這是它抗 GPU 明顯優於 PBKDF2 的原因。它不是為了這件事設計的——bcrypt 比通用 GPU 運算早了將近十年,抗 GPU 是事後才顯現的性質;而這也解釋了為什麼這個優勢不會跟著硬體變大而長,因為它從來不是一個被調整過的參數。
判別一個沒見過的構造適不適合用來存密碼,用同一組問題:它對什麼收費、那一項調不調得動、調高之後攻擊者的平行度會不會跟著降。三個問題都有答案的才進入「演算法選型」的比較。
演算法選型
現行的預設是 Argon2id。依「攻擊者的硬體優勢從哪裡來」的框架,它在時間、記憶體與存取形態三個位置都收費,其中記憶體那一項是目前對訂製硬體最有效的收費位置。
選它要付三樣:每次登入的記憶體佔用變成容量約束(算法見「work factor 怎麼定」的併發尖峰那一步)、三個參數可以配出「看起來設定過但實際更弱」的組合而沒有任何訊號會提醒、以及跨語言的函式庫品質差異比 bcrypt 大。bcrypt 在這三項上都相反——單一整數參數配不錯、二十餘年的部署史、跨語言實作品質一致,這也是它在下方仍被列為可接受選擇的理由。
scrypt 是 Argon2id 的函式庫在執行環境不可用時的備選,它同樣對記憶體收費。選它的時候參數取公開建議的下限起跳(N=2^17、r=8、p=1),再依「work factor 怎麼定」的量測往上加。
bcrypt 仍然是可接受的選擇,判準是既有系統已經在用、或執行環境的函式庫支援度是決定因素。它的記憶體項固定且不可調(成因見「攻擊者的硬體優勢從哪裡來」),所以抵抗訂製硬體的能力弱於前兩者。它的長處是成熟、實作陷阱少、幾乎所有語言都有經過檢驗的函式庫。既有系統用 bcrypt 而參數在建議下限之上時,換演算法的優先序低於其他資安工作。
PBKDF2 的適用情境是合規要求限定在通過特定認證的演算法清單內。它的抗硬體能力最弱,選它通常是外部約束而非技術判斷。選它的時候要注意合規清單規範的是可用哪些演算法與迭代次數的下限,上限來自登入延遲預算、與合規無關——把迭代次數推到延遲預算允許的上限,下限取 600,000 次(PBKDF2-HMAC-SHA256)。判別自己受不受這個約束的方法見「合規清單怎麼讀」。
「攻擊者的硬體優勢從哪裡來」的三個問題用在具體構造上,判的是成本可不可調;另有一條獨立的加分項是有沒有一把存在資料庫之外的金鑰(見「資料庫之外的那一把金鑰」)。直接使用一般用途的雜湊(單次 SHA-256、SHA-3)兩項都沒有,速度快正是這一類要避免的性質——注意判別點在構造而不在演算法家族,PBKDF2-HMAC-SHA256 內部就是迭代的 SHA-256,它合格是因為迭代次數可調。自製的多輪雜湊組合缺少公開檢驗,實際強度沒有人算得出來,即使成本看起來可調。
把密碼加密後存起來是另一回事,不屬於這一類原語。還原能力在密碼儲存上是要消除的性質,但確實有系統必須持有可還原的祕密——要拿它去對下游系統認證的情境(密碼管理器、舊協定的代理層、部分金融整合)。那屬於 credential 保管而非使用者密碼儲存,判讀軸移回金鑰放在哪裡,見 7.28 密碼學原語選型。
合規清單怎麼讀
合規文件分兩類,約束力不同,而把兩類當成同一件事是這個題目最常見的誤讀。
指引類回答「該做什麼」。 NIST 的數位身分指引(SP 800-63B,第四版於 2025 年 8 月發布)要求密碼加鹽、並用合適的密碼雜湊方案儲存,而它不點名任何演算法——第三版曾經列過名字,第四版把它們全部拿掉,改成「應該使用 SP 800-132 最新版或後續 NIST 指引裡核准的方案」,而且那句用的是「應該」而非「必須」。這一類文件給的是要求與理由,它不負責告訴讀者哪一個函式庫通過了哪一項驗證。
驗證類回答「可以用哪幾個具體演算法」。 FIPS 140-3 的核准演算法清單透過 SP 800-132 涵蓋 PBKDF2;Argon2、bcrypt 與 scrypt 都在清單之外。需要使用經過 FIPS 驗證的密碼模組時就沒有選擇權——PBKDF2 是唯一走得通的路,而這正是「演算法選型」把它的適用情境寫成外部約束的原因。
兩份文件的關係比「一份寬鬆一份嚴格」更繞,值得單獨寫清楚。指引沒有禁止 Argon2——它根本沒有提到任何演算法,而且它指向核准清單的那句是建議而非要求。於是「用 Argon2 不違反 NIST 指引」與「Argon2 不在 NIST 核准的清單裡」兩句話同時為真,把其中任一句單獨拿去當結論都會推出錯的決定。實際的約束因此不來自指引本身,而來自有沒有一份契約或法規要求密碼模組經過 FIPS 驗證。
判別自己受哪一類約束,問的是有沒有一份契約或法規要求密碼模組經過 FIPS 驗證。聯邦政府採購與部分金融、醫療的稽核項目會要求,多數商業服務不會。答案是「不確定」時要去問合約與稽核那一端,因為這個約束來自外部而不是工程判斷,而它一旦成立就直接鎖死演算法選項——選型做完才發現要重來的成本遠高於事前問一次。
受 FIPS 約束時本章其餘各節照常適用,變的只有演算法固定這一項。迭代次數這裡要小心兩個下限不是同一個:合規清單訂的是法規可接受的下限(SP 800-132 給的是 1,000 次),安全下限另取公開建議的現行值(本章「work factor 怎麼定」用的 600,000 次),兩者取大。只滿足前者的設定在稽核上通過,而在「猜一次要付多少代價」這條軸上比本章其餘各節的水準低了好幾百倍。上限仍然由登入延遲預算決定,複查節奏、參數老化的形態與升級路徑都不變。
這些清單本身會改版,涵蓋範圍也會變。寫進設計文件的應該是「本系統受哪一類約束」這個判定,具體的演算法清單與迭代次數在每次複查時回原始來源查一次。
work factor 怎麼定
量測決定的是要往上加多少,不是要不要往下調——先取公開建議值當地板,量測只用來決定加多少。這一點是這一節的前提:量測本身只定得出上界(延遲預算),慢機器上量出來的「合規值」可能遠低於安全底線,而沒有地板時讀者無從判斷量出來的值算不算太低。函式庫的預設值與公開建議值是兩個獨立來源,不保證一致。實際存在照官方寫法呼叫就落在地板之下的組合,常見於框架內建的 PBKDF2 編碼器、沿用多年沒有調整的預設常數、以及不要求指定迭代次數的低階金鑰衍生 API。這是下面「讀出現行的實際值」那一步存在的理由——用預設不等於用建議值。
寫作當下的地板是 Argon2id m=19456(記憶體,單位 KiB)/t=2(迭代次數)/p=1(平行度),或等價的記憶體與時間組合;scrypt N=2^17(成本參數)/r=8(區塊大小)/p=1;bcrypt cost 10;PBKDF2-HMAC-SHA256 600,000 次迭代。這組數字取自 OWASP 的密碼儲存指引,而它本身也會過期——地板隨硬體變快而上調,速度比任何一份文件的改版都快。用之前先回原始來源查一次現行值,別直接抄這裡的數字。
多數讀者不是從零開始設參數——系統已經在跑,而且已經有一組參數在運作,它來自函式庫預設。所以第一步是讀出現行的實際值,而讀法不是查設定檔也不是查文件:設定檔可能沒有覆寫預設,文件的版本可能與線上跑的不符。可靠的做法是從產出的雜湊字串反讀——Argon2 與 bcrypt 的輸出把實際參數編在字串裡($argon2id$v=19$m=…,t=…,p=…$…、$2b$12$…),在正式環境註冊一個測試帳號、取出那筆雜湊的前綴,得到的就是這套系統真正在用的值。PBKDF2 沒有通行的自描述格式,要回查該實作的常數。
讀出來之後對地板,再量測決定往上加多少。加多少取決於自己的硬體與可接受的登入延遲。
- 在正式環境的機器規格上量單次雜湊耗時。開發機的數字會誤導,而且誤導方向固定:現代開發筆電的單核效能通常高於共享型的雲端 vCPU,所以在開發機上量出來的參數搬到正式環境,先爆掉的是登入延遲。
- 目標落在登入延遲預算內。這個預算由產品決定,而常見的上限抓在一秒以內;登入是低頻操作,這個量級多數服務吃得下。高頻呼叫的內部服務要另外處理,通常做法是登入一次換成 session、不在每次呼叫重算。
- 把並行登入的尖峰算成兩項,因為記憶體硬化演算法的瓶頸多半在記憶體而不在 CPU。CPU 項是尖峰併發數乘上單次耗時,再乘上
p——p=1時一次雜湊約佔一個核心,而主流函式庫的 Argon2 預設多半不是 1,照預設跑的話這一項要放大數倍。p也被編進雜湊字串、驗證時必須用同一個值,所以它不是事後可以調的效能旋鈕;記憶體項是尖峰併發數乘上m——Argon2id 在m=19456時每次雜湊常駐約 19 MiB,五百個併發登入就要準備約 9.5 GiB,只算 CPU 的話機器會在尖峰時因記憶體不足而倒。這兩項是選 Argon2id 要付的營運代價,選型階段就該知道。 - 記下量測的日期與當時的機器規格。這一項讓下一次複查有比較基準,缺了它複查會退回重新猜。
地板值在自己的機器上就已經超過延遲預算時,這四步走不出答案,出口有三個:加機器(把單位成本換成硬體支出)、退到地板較便宜的 bcrypt cost 10(換演算法強度)、或提高延遲預算(換使用者體驗)。三者之間的取捨由產品決定,工程這一端能做的是把三個數字都算出來。
複查有兩個觸發,缺一個就會漏掉一半的老化。第一個跟著自己的硬體走:換過機器、升級過雲端執行個體規格、或距離上次量測超過一到兩年,就重量一次——這條由延遲預算自動把參數往上推。第二個跟著公開建議值走:地板本身上調而自己沒換機器時,第一個觸發不會發動,所以複查時要同時回原始來源對一次現行地板。
資料庫之外的那一把金鑰
前面所有的判讀都建立在「攻擊者手上有雜湊值與 salt」這個前提上,而 salt 就存在同一張表裡、跟著雜湊一起被拿走。Pepper 加的是一把不存在資料庫裡的金鑰,讓外洩的資料本身不足以開始猜——沒有那把金鑰,任何猜測都算不出可以比對的值。
它的有效範圍要先講清楚,因為這一層的保護是條件性的:只在資料庫與金鑰分開淪陷時成立。SQL 注入取走整張表、備份檔案外流、唯讀副本被存取,這幾種是它擋得住的典型形態。整台伺服器被拿下、或應用程式的執行權限被取得時,金鑰跟著走,這一層歸零。判斷要不要加,看的就是威脅模型裡前一類路徑的權重。
金鑰的住址決定它的實際強度,三種常見選擇的差別在於攻擊者取得執行權限之後還剩下什麼。放在應用程式的設定檔或環境變數時,執行環境一旦被取得,金鑰立刻可讀。放進外部金鑰管理服務時,金鑰不落在應用程式的磁碟上,但應用程式有權呼叫,取得執行權限的攻擊者一樣拿得到——差別在於他必須留在線上一次次呼叫,而那些呼叫留下紀錄、也可以被限速。放進硬體安全模組時,金鑰不離開模組、運算在模組內完成,性質與前一項相同而金鑰本身不可導出。後兩者換到的是偵測與限速的機會,見 HSM 與 Key Management。
輪替的成本由那一層是不是可逆的決定,而不是由它混在前面還是後面決定。這條軸分不開的話會得出「pepper 難輪替」這個過度概括的結論,也會對最常見的那一種做法給出相反的答案。下面三種是常見形態而非窮舉——遇到沒列到的接法,用可逆性這條軸自己歸位即可。
不可逆、混在輸入端:先把密碼與 pepper 一起做 訊息驗證,再把結果餵給金鑰衍生函式。換金鑰需要原始密碼,只能等使用者登入,與換演算法的第一條路徑同型。
不可逆、疊在輸出端:照常做完金鑰衍生,再對它的輸出做一次訊息驗證。這是公開指引裡最常建議的形態,而它的輪替成本與上一種相同——訊息驗證的輸出推不回它的輸入,因此拿不到內層結果,一樣要等使用者登入。位置換了而可逆性沒換,成本就沒有換。
可逆、包在輸出端:照常做完金鑰衍生,再用金鑰把結果加密起來存。換金鑰只要用舊金鑰解開、用新金鑰重新包上,全庫可以離線完成,不需要任何人登入。這條路徑把輪替從「等使用者回來」變成「跑一次批次作業」,而那次批次在大帳號庫上就是「帳號庫大的時候這兩條路徑怎麼跑」講的全表改寫,約束一樣適用。代價有兩層:金鑰的保管責任更重,以及那一層帶進加密自己的失效形態——用確定性的加密或重複使用同一個初始向量時,「兩個帳號的密碼相同」會變得看得出來,而那正是 salt 要消除的性質。
兩種做法都要在紀錄旁存一個金鑰版本識別,否則輪替期間新舊並存就沒有依據判斷該用哪一把驗證。
導入前要定案的是金鑰本身的保管與備援,因為遺失 pepper 的後果是全部密碼都無法驗證,唯一的出口是全體使用者重設。這個後果的量級高於本章其他任何一項決定,所以它的備份、存取權限與復原演練要在上線前確認,而不是等第一次輪替時才處理。
參數老化的形態與失效
參數老化出現在上線之後沒有再動過密碼設定的系統。這一類的成因與疏忽無關:參數當初定得正確,而它的強度是相對於外部硬體定義的,系統自身的運作完全不依賴它。識別特徵是問「上一次調整 work factor 是什麼時候」,答案是「不知道」或「應該是上線的時候」。
這一類的失敗長這樣:服務上線時照當時的建議值設了參數,量測過、也在延遲預算內。幾年之間機器換過兩輪、雲端執行個體規格升過級,密碼設定沒有人動過,因為它沒有壞。資料庫外洩之後才發現,當初設計成「猜一次要付的代價」在現在的硬體上已經降到原本的一小部分,而真實帳號庫裡總有一批弱密碼,這個差距因此轉成可還原的帳號數量。查不出來的原因是參數老化沒有任何訊號——登入正常、監控正常、沒有任何一項檢查會回報「這個值已經過時」,它只在被攻擊的那一刻才顯現強度。補救要走使用者下次登入的路徑(見「升級路徑」),所以止血速度由使用者的回訪頻率決定,而不是由自己的部署速度決定。
升級路徑
密碼的明文只在使用者輸入的那一刻存在於系統裡,所以升級有兩條路徑,差別在能不能不等使用者回來。
登入時重算是乾淨的那一條:使用者登入、用舊參數驗證通過、隨即用新參數重新雜湊並覆寫。它產出的是單層雜湊、強度分析單純,代價是進度跟著使用者的回訪走,長期不登入的帳號會一直留在舊參數上。
全庫包一層是快的那一條:把舊雜湊值當成新演算法的輸入再算一次(例如對既有的 md5 結果整批跑 Argon2id),不需要知道明文,因此全庫可以立刻完成。它的代價是格式變成複合結構、強度分析要同時考慮兩層,而且外層的輸入是固定長度的雜湊值而非原始密碼。這條路徑要配登入時降回單層當收尾。
判準是急不急。參數只是落後於現行建議、沒有外洩跡象時走第一條就夠;外洩已經發生、或參數落後太多時先走第二條打底,再用第一條逐步收回單層——這一條決定了止血速度握在自己手上,而不是由使用者何時回來決定。
參數變更還有一種不是自己發動的形態:執行環境升級。有些語言的預設密碼雜湊參數會隨版本上調,升上去之後新產生的雜湊用新參數、既有的仍是舊參數,於是同一張表裡自然長出混合格式。這個方向與參數老化相反(保護自動變好),但它同樣需要收尾——多數這類實作附有「這筆雜湊是否需要重算」的判斷函式,把它接在登入驗證成功之後,就是「登入時重算」那條路徑的現成觸發點。
兩條路徑都要配一個收尾動作:設一個期限,期限後仍未重算成單層的帳號強制走密碼重設流程。期限長度由帳號庫的回訪分布決定,而這個分布要先查得出來——查不出來時,期限只能落在沒有依據的估計上,而這種估計通常偏長。
儲存格式要能同時容納新舊參數,否則升級無從進行。現行的做法是把演算法識別、參數與 salt 一起編碼進雜湊字串(Argon2 與 bcrypt 的標準格式都是這樣),驗證時從字串本身讀出該用哪組參數。既有系統若把參數寫死在程式碼裡而非存在紀錄旁邊,升級的第一步是先改儲存格式。
帳號庫大的時候這兩條路徑怎麼跑
「升級路徑」的兩條在小帳號庫上是選擇題,到了千萬級的帳號庫上變成排程題——約束從密碼學移到資料庫與使用者行為分布。
全庫包一層在大庫上是一次全表改寫,它的瓶頸落在讀寫放大與複寫延遲上。批次大小要由複寫延遲的容忍度決定,而不是由「幾天之內跑完」這個目標倒推;監控盯的是延遲曲線,進度百分比只用來估剩餘時間。
順序有一項容易做反:格式識別要先上線並跑一段時間,才開始改寫。改寫一開始,同一張表裡就同時存在包過與沒包過的紀錄,驗證邏輯要能從紀錄本身判斷走哪一條。識別邏輯與改寫同時上線的話,改寫期間出現的驗證失敗無法區分是格式判斷寫錯還是資料真的壞了,而那正是最需要快速判斷的時刻。
回退能力要在開始前確認。外層可以拆掉的前提是外層用的參數與金鑰都還在且可用——確認過了這條改寫才是可逆的,沒確認的話它是單向操作,而單向操作在千萬筆的規模上沒有試錯空間。
登入時重算在大庫上的問題是尾巴。 回訪分布通常是長尾:前兩成的活躍帳號幾天內就收完,剩下的可以拖上幾年。所以期限與強制重設那個收尾動作在這個規模上是必要的,而期限要按分布的分位數定——先查出「多少比例的帳號在 N 天內登入過」這條曲線,選一個分位數當期限,落在後面的走重設流程。憑直覺定期限時,得到的值通常偏長,理由是估的人心裡想的是活躍使用者。
還有一項只在大庫上出現:強制重設本身是一個容量事件。期限到期時把剩下的帳號一次全部標記,這批人下次登入會同時湧進密碼重設流程,而那條流程通常包含寄送信件——郵件發送的配額、客服的詢問量、以及重設頁面的流量會在同一段時間一起上升。處置是把到期日分批錯開,讓這個事件攤平成一段時間而非一個時點。
常見風險邊界
- 密碼設定的參數寫死在程式碼裡、而非隨每筆紀錄儲存時,代表升級路徑不存在,這是比參數值偏低更優先的問題。
- 登入延遲預算沒有人定過時,參數會落在「不會被抱怨」的位置,而那個位置由最沒耐心的那條回饋路徑決定,與安全需求無關。
- 密碼雜湊在每次 API 呼叫都重算時,代表 session 機制缺位,參數會因為效能壓力被迫調低。
- 同一套帳號庫裡存在多種雜湊格式而沒有升級期限時,代表升級啟動過但沒有收尾,最弱的那一批會無限期留著。
- 用 bcrypt 而沒有限制輸入長度時,超過 72 bytes 的部分不參與計算。各實作對這件事的處理不同:多數靜默截斷,少數回傳錯誤。自己那一端沒有報錯並不代表沒有發生,所以長度限制要在應用層明確做——鼓勵長 passphrase 的系統,使用者以為的強度與實際強度從第 73 個位元組起脫鉤。
- 威脅模型設定在「資料庫被拿走」而沒有評估過把金鑰放在資料庫之外時,少的是 pepper 那一層,取捨見「資料庫之外的那一把金鑰」。已經有 pepper 而確認不了那一層是不是可逆的時候,輪替成本還沒有被算過——不可逆的那兩種由使用者回訪決定完成時間,可逆的那一種由自己的批次決定。
- 帳號庫規模到了千萬級而升級計畫只寫了「登入時重算」時,尾巴的處置缺位;期限、分位數依據與強制重設的容量影響見「帳號庫大的時候這兩條路徑怎麼跑」。
下一步路由
- 從需求面回頭確認範圍:0.8 資安與資料保護需求 的「密鑰與秘密」議題
- 原語分類與金鑰位置判讀:7.28 密碼學原語選型
- 登入節奏、多因子與會話收斂:7.2 身分與授權邊界
- 外洩之後的止血與通報:密碼外洩特有的處置(強制重設的範圍怎麼定、通知門檻在哪)見 7.37 密碼外洩之後;事故節奏的通用形態見 8.x 止血與回復策略
- 密碼雜湊的運算容量:本站尚未寫把認證運算納入容量規劃的章節;在那之前用上方「work factor 怎麼定」第 3 步的算法(尖峰登入量乘上單次耗時)自行推估。