安全面試中的一個基礎(chǔ)問題:你如何在數(shù)據(jù)庫中存儲密碼?
3分鐘講解。
上周的面試故事
職位:初級安全工程師,剛畢業(yè)。
開始面試。
我:“這里你提到對數(shù)據(jù)安全有很好的理解。你能舉例說明哪些方面的數(shù)據(jù)安全嗎?”
A:“當然。例如,當我們構(gòu)建一個系統(tǒng)時,會有一個用戶模塊,當我們在數(shù)據(jù)庫的用戶表中存儲密碼時,我們需要在存儲之前對其進行加密?!?/p>
我:“你確定是加密而不是哈希處理嗎?”
A:“是的。”
我:“那你把密鑰存在哪里?”
A:“什么密鑰?”
我:“你用來加密密碼的加密密鑰。是對稱加密還是非對稱加密?每個用戶都有一個密鑰還是共享一個密鑰?”
A:“嗯……我們沒有使用這些。所以應(yīng)該是哈希處理。”
我:“沒關(guān)系。你能解釋一下為什么我們需要在存儲之前進行哈希處理,而不是存儲明文嗎?”
A:“是的。因為我們想要確保安全。當我們驗證密碼時,密碼不能以明文形式從UI發(fā)送到服務(wù)器進行驗證?!?/p>
我:“當你注冊用戶時,是否需要以明文形式發(fā)送密碼和確認密碼?”
A:“是的,但那是唯一一次我們發(fā)送明文?!?/p>
我:“好的,我明白你的意思。你認為在數(shù)據(jù)庫中存儲哈希值的原因是為了減少在API調(diào)用期間發(fā)送敏感數(shù)據(jù)為明文的機會?”
A:“沒錯?!?/p>
我:“那么你認為當數(shù)據(jù)庫被人入侵時,存儲哈希值會有意義嗎?”
A:“哦,是的,這樣原始密碼就不會被黑客看到。哈希值可以防止這一點。”
我:“是的,但這還不夠。有人仍然可能使用彩虹表攻擊,通過哈希碰撞得到原始值。你知道什么是鹽(salt)嗎?”
A:“聽說過。值應(yīng)該是一個隨機字符串,這會使密碼存儲更安全。”
我:“很好。你知道它是如何工作的嗎?”
A:“不太清楚?!?/p>
我:“假設(shè)有人通過SQL注入或其他方式獲取了數(shù)據(jù)庫,然后使用彩虹表進行攻擊,發(fā)生哈希碰撞并得到了原始文本,但他得到的是混合了隨機字符串的原始密碼,這樣我們可以防止原始密碼被竊取。這意味著我們阻止了一些人獲取你的‘一鍵登錄’密碼來登錄你的郵件、社交媒體甚至銀行賬戶?!?/p>
A:“我明白了?!?/p>
我:“那么你們目前系統(tǒng)中使用的是哪種哈希算法?”
A:“我們知道m(xù)d5不安全,所以我們使用了其他選項,比如sha3。”
我:“你能簡要說明一下為什么md5不安全嗎?為什么選擇sha3來哈希密碼?因為據(jù)我所知,sha3的KDF(如hmac-sha3)并不是為哈希密碼設(shè)計的,而是用于其他用途,如JWT令牌簽名?!?/p>
A:“md5被證明是不安全的,已經(jīng)有一些報道。但是KDF是什么?”
我:“KDF是密鑰派生函數(shù)。這意味著你使用的哈希算法會將密碼作為輸入生成哈希值。是的,有報道說md5不安全。但你知道為什么嗎?”
A:“嗯,不太清楚?!?/p>
我:“簡而言之,這是因為它的哈希碰撞抵抗力弱,可能被彩虹表攻擊?!?/p>
A:“什么是彩虹表?”
我:“彩虹表是一種預計算的表格,包含從明文密碼派生的哈希值鏈。這些鏈通過反復應(yīng)用哈希函數(shù)生成,然后通過一系列還原函數(shù)將結(jié)果哈希值還原為明文密碼。彩虹表通常用于密碼破解攻擊中,攻擊者將存儲在被入侵數(shù)據(jù)庫中的哈希密碼與彩虹表中的條目進行比較,以找到匹配的明文密碼。”
A:“我明白了?!?/p>
我:“那你為什么使用sha3來生成密碼?”
A:“我覺得它比MD5更安全?!?/p>
我:“是的,但可能有更好的密碼哈希選項。比如2015年獲獎的argon2,比sha3需要更多的迭代和更高的RAM,這意味著更高的時間復雜度和資源消耗,從而使攻擊者更難以進行暴力破解。但sha3通常用于其他用途,例如數(shù)字簽名,平衡安全性和性能?!?/p>
……
讓我們逐個分析這個面試中的細節(jié)。
密碼哈希
我們都知道,在21世紀,密碼絕不能以明文形式保存。
然而,這不僅僅是為了減少在某些API調(diào)用期間發(fā)送密碼值為明文的機會。 主要的關(guān)注點是當數(shù)據(jù)庫落入壞人之手時,使其無法恢復原始文本。 接下來發(fā)生的事情是數(shù)據(jù)庫將被“憑證填充攻擊”。相同的憑證會在不同的賬戶上反復嘗試:電子郵件、銀行卡、社交媒體、學校,甚至是政府賬戶。
為什么不使用加密而是哈希?
關(guān)鍵區(qū)別在于可逆性。加密是一個可逆的過程,這意味著使用正確的密鑰可以恢復原始的明文密碼。這構(gòu)成了一個安全風險,即使加密密鑰被安全管理。 此外,管理加密密鑰,如構(gòu)建密鑰鏈,可能會帶來顯著的成本和復雜性。
為什么需要鹽(salt)
鹽用于為每個哈希密碼添加隨機性,使攻擊者使用預計算表如彩虹表來破解密碼變得計算上昂貴。 沒有鹽,攻擊者可以通過將哈希值與大型預計算哈希數(shù)據(jù)庫進行比較來有效猜測密碼。 此外,鹽必須對每個密碼唯一,而不僅僅是對每個用戶唯一。這確保即使兩個用戶有相同的密碼,由于唯一的鹽,他們的哈希值也會不同,防止攻擊者輕易識別重復的密碼。
不同用途的哈希
Sha3比md5更安全,為什么不使用sha3?哈希不僅用于密碼值,還有不同的使用場景:
- 數(shù)據(jù)完整性(如SHA3):哈希算法如SHA3通常用于指紋文件,確保其完整性保持不變。這些算法需要在速度和哈希碰撞抵抗之間找到平衡。雖然SHA3提供了良好的碰撞抵抗,但它也保持了文件指紋任務(wù)的合理性能。
- 校驗和(如CRC32):當速度至關(guān)重要時,如在網(wǎng)絡(luò)傳輸期間驗證數(shù)據(jù)完整性,CRC32等算法表現(xiàn)出色。CRC32通常用于快速識別文件在傳輸過程中是否被修改,優(yōu)先考慮效率而不是碰撞抵抗。
- 密鑰生成(如Argon2、bcrypt、PBKDF2):為了安全地存儲密碼或生成加密密鑰,首選專門的密鑰派生函數(shù)(KDF)如Argon2、bcrypt或PBKDF2。這些算法故意引入“慢”哈希過程,通過增加計算復雜性來抵御哈希碰撞的暴力破解。
雖然SHA3適用于HTTPS連接或JWT令牌簽名生成等任務(wù),但它缺乏提供密碼哈希所需的必要“慢”特性。
為什么KDF在密碼哈希中很重要
KDF在密碼哈希中通過故意減慢哈希過程起著關(guān)鍵作用。這種故意的減慢是通過多次迭代哈希值計算實現(xiàn)的。雖然這些迭代的技術(shù)細節(jié)可能很復雜,但它們本質(zhì)上涉及重復操作如移位、反轉(zhuǎn)和XOR-ing哈希值,執(zhí)行數(shù)千輪。
使用KDF哈希密碼的主要目標是:
- 減慢過程:通過引入計算開銷,KDF使攻擊者更難以暴力破解密碼。計算哈希值所需的時間和資源增加,作為快速詞典或彩虹表攻擊的威懾。
- 資源消耗:KDF在哈希過程中還會消耗大量的CPU和內(nèi)存資源。這種資源密集型特性進一步阻礙了攻擊者,因為他們必須投入大量的計算能力和時間來破解哈希密碼。
什么是彩虹表攻擊?
簡要解釋:
- 詞典表:在彩虹表之前,攻擊者預計算所有哈希值,并在一個稱為“詞典表”的表中枚舉所有哈希值。然后,在獲取目標數(shù)據(jù)庫表后,進行哈希碰撞并得到關(guān)聯(lián)的原始密碼值(這也是為什么鹽如此重要)。
- 然而,詞典表可能會迅速增長,最終包含大量行,變得不可用。
- 彩虹表可以被視為“詞典表的高效版本”。
- 在彩虹表中,有兩個函數(shù)。H:從明文獲取哈希值,R:從哈希值獲取明文。
- 存儲多行,每行是一個“哈希鏈”,每行從一個明文開始,然后多次進行H和R。
- 對目標數(shù)據(jù)庫表進行哈希碰撞,一旦哈希值匹配,進行R以獲取明文。
最后
為什么md5(或sha1)不能用于密碼哈希?如果可以,md5還有其他用途嗎?
簡單回答:弱哈希碰撞抵抗。
- md5在2004年被證明具有弱哈希碰撞抵抗。
- sha1也在2005年被證明具有相同的問題,并由谷歌在2017年宣布。
- 我們可以將md5用于其他用途嗎?比如指紋或校驗和?不行。已被證明,兩個不同的文件可以生成相同的md5;如果用于校驗和,crc32具有更快的性能。
- md5沒有使用場景,你永遠不應(yīng)該使用它。