允許多個密碼重置令牌,以及關於隨機數可預測性的問題
我對密碼重置系統有一些要求,最後還有一些關於其安全性的問題。
要求
目的是提供滿足以下屬性的密碼重置機制:
- 每個使用者有多個令牌,以允許電子郵件延遲不會引起混淆。
- 令牌是一次性的並且會過期。
- 對重置資訊數據庫具有隻讀訪問權限的人無法輕鬆重置使用者密碼。
- 抵抗重放攻擊、定時攻擊和(可能)不安全的隨機數生成器。見下文。
假設/細節
- 有一個在應用程序之外不會知道的秘密隨機值。
- 一旦使用者使用密碼重置令牌重置其密碼,使用者的所有可用令牌都會從數據庫中刪除。
大綱
請注意,
||
這裡是串聯。給出的方法是Devise身份驗證庫的修改版本。具體來說,修改是現在有多個token。讓我們
S
提前選擇一個固定的秘密。要為使用者構造一個重置令牌
U
,請生成一個(安全)隨機數r
並考慮該對(id, r)
其中
id
從遞增的計數器值中選取。產生S' = PBKDF2(S)
加鹽,讓
S'
成為 HMAC 值的關鍵h = HMAC(S', id || r).
儲存重置資訊
(U, id, h, e),
與
e
到期時間,在發送(id, r)
給使用者之前。當有人嘗試重置密碼時,他們會為我們提供使用者提供的 the
id
和 for值r
:(id_user, r_user).
用於
id_user
查找以前儲存的(U, id, h, e)
資訊。如果
id_user
對應沒有這樣的資訊,則無事可做。如果e
是過去,則令牌已過期。再次生成
S' = PBKDF2(S)
,並比較HMAC(S', id_user || r_user)
與之前的 h 值,即
HMAC(S', id || r).
如果這兩個值匹配(見下面的註釋),那麼令牌是有效的,我們允許使用者
U
重置他們的密碼。當他們這樣做時,所有儲存的重置資訊((U, id, h, e)
給定的條目U
)都將被刪除。筆記
希望前幾個要求顯然得到滿足(多個令牌等)。值得解釋的主要問題可能是:對重置資訊數據庫具有隻讀訪問權限的人無法輕鬆重置使用者密碼。
因為令牌值本身並沒有儲存在數據庫中,只有對應的HMAC,能夠讀取數據庫的人無法構造一個有效的重置密碼URL。
至於上面的相等性檢查:如果使用者篡改 的值
(id, r)
,他們將生成一個完全不同的值,h
該值應避免暴露時序資訊。包含
S' = PBKDF(S)
(使用鹽)是為了使 HMAC 值的生成成本更高,因為令牌應該具有與密碼相似的屬性。允許可預測的隨機數
如果隨機數生成器曾經生成攻擊者可以預測的數字,那麼很容易找到正確的 ID 值,從而獲得有效的重置連結。
至少在撰寫本文時,一些數字生成器依賴於使用者空間算法,這可能是一個問題:http ://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/
為了對此保持偏執(相關係統涉及各種個人資訊)並解決可預測隨機數的問題,我們可以嘗試將 HMAC 值發送給使用者,以便我們驗證真實性。
之前,我們發送
(id, r)
給使用者,這是一個遞增的 ID 計數器值和隨機數。相反,發送使用者
(id, r, h).
在處理重置請求時,我們會收到
(id_user, r_user, h_user).
我們可以取這個值並通過檢查是否等於 來驗證 既沒有
id_user
也r_user
沒有被篡改。h_user``HMAC(S', id_user || r_user)
完成該驗證步驟後,我們可以像以前一樣使用
id_user
查找(U, id, h, e)
和繼續。如果攻擊者曾經預測過隨機數
r
(因此(id, r)
),他們將不知道如何生成有效的 HMAC,因為它需要S'
哪個是秘密的,並且這對(id, r)
是以前從未見過的。請注意,我們從不用於
h_user
查找(U, id, h, e)
資訊,因此數據庫不應該在其查找算法中暴露時間攻擊。奇怪和問題
- 它真的可以抵禦定時攻擊、重放攻擊等嗎?這感覺是邊緣滾動你自己的加密貨幣。有問題嗎?
- 有沒有更簡單的方法?
- 允許攻擊者生成任意數量的重置令牌會降低哪些安全屬性,因為我們允許存在多個令牌並且任何人都可以發送重置?
- 嘗試防禦表面上安全的隨機數生成器假設的未來錯誤是否不合理?(編輯:為清楚起見重新措辭。)
- 如果這個方案對可預測的
r
值是安全的,那麼它不等於只使用(id, h)
轉換id
為 GUID/UUID 的提供嗎?更新:
我懷疑嘗試防禦例如 OpenSSL 隨機性錯誤實際上是不合理的,這導致我遇到上述問題的另一個問題:在沒有 HMAC 的情況下簡單地儲存
(U, PBKDF2(r))
和發送給使用者有什麼問題?r
如果有足夠的位,r
那麼考慮到令牌在 <= 6 小時內到期,這還不夠安全嗎?
有沒有更簡單的方法?
我認同。
讓伺服器以如下形式發送一個重置令牌:
(deadline, HMAC(server key, id_user || S || deadline))
也就是說,伺服器計算一個允許
id_user
從S
以前更改其秘密的身份驗證器deadline
。使用的密鑰應該只有伺服器知道並且可以經常更改,因為更改它只會使目前活動的重置令牌無效。(您可以將
S
其用作 HMAC 密鑰,但是重置令牌將允許對 進行暴力攻擊S
,這可能是不可取的。現在它不允許在不知道伺服器密鑰的情況下進行攻擊。)要實際重置密碼,使用者必須提供上述令牌,他們的
id_user
(如果需要,可以是令牌的一部分)——S
當然還有一個新的。伺服器根本不需要重置令牌的數據庫,因為他們需要的所有資訊都包含在使用者發送的令牌中。它驗證最後期限是否在未來,然後重新計算 HMAC 並與令牌中的進行比較。
您的要求是:
- 每個使用者有多個令牌,以允許電子郵件延遲不會引起混淆。
- 令牌是一次性的並且會過期。
- 對重置資訊數據庫具有隻讀訪問權限的人無法輕鬆重置使用者密碼。
- 抵抗重放攻擊、定時攻擊和(可能)不安全的隨機數生成器。見下文。
- 伺服器可以創建任意數量的重置令牌,使用者可以在截止日期之前使用任何一個。
- 如果使用者更改了他們的
S
,即使仍然有效的令牌也將不再起作用。- 沒有重置資訊數據庫,所以不是問題。
- 由於與 2 相同的原因,重放攻擊不起作用。為了避免定時攻擊,伺服器所做的所有比較都應該是恆定時間。完全確定性,所以 RNG 無關緊要。
與您的原始提案相比的優勢:
- 更容易實現和推理。
- 更快,因為沒有 PBKDF2。
- 沒有可以被攻擊的數據庫。
- 不需要每個令牌的隨機性。
您的更新要簡單得多,這很好,但其他三個因素仍然存在。您還可以通過使
r
不需要 PBKDF2 足夠大來修復“更快”,但是如果您發現有問題,這會使它使用更多的隨機性。
- $ ;;; $ 可能只要你比較安全,雖然你正在申請
$ ;;; $ 一個 pbkdf 到什麼應該是一個均勻隨機的長密鑰。 2. $ ;;; $ 不要打擾計算
S'
;letS
是一個均勻隨機的密鑰並使用它。$ ;;; $ 要為使用者構造重置令牌
U
,請設置h = HMAC(S, 0 || (id,U,e) )
,$ ;;; $ 在發送前 設置
H = HMAC(S,1||h)
並儲存復位資訊(id,e,H,U)
$ ;;; $
(id,h)
給使用者。 $ : $ 當有人試圖重置他們的密碼時,他們會給你使用者提供的$ ;;; $
(id_user,h_user)
分別為id
和 的值h
。 $ : $ 用於id_user
查找$ ;;; $ 以前儲存
(id,e,H,U)
的資訊。 $ : $ 如果 id_user 對應沒有這樣的資訊,$ ;;; $ 那麼就沒有什麼可做的了。如果 e 在過去,則令牌已過期。
$ ;;; $ 比較
HMAC(S, 1 || h_user )
,H
這是HMAC(S, 1 || h )
.$ ;;; $ 如果這兩個值匹配,則令牌有效 … 。 $ : $ …
$ ;;; $ 如果重置綁定到一個 id 僅僅是偶然的,而不是
$ ;;; $ 所需的功能,那麼您可以使用
(U,e)
而不是(id,U,e)
. 3. $ ;;; $ 你只是失去了量化的安全性。 $ : $ (攻擊者可以對 HMAC 進行更多查詢。) 4. $ ;;; $ 不,因為這很容易做到。 5. $ ;;; $(id, h)
即使id
變成GUID/UUID 也不等同於只使用,$ ;;; $ 因為只要您重用任何
id
GUID/UUID的.