存儲設計
設計原則: 密碼存儲必須杜絕獲取明文之可能
用戶的密碼通常具有一定規(guī)律,一般用戶會對若干不同賬戶使用相同密碼,不論以何種方式導致明文泄漏,就意味著用戶的其他一系列賬戶也受到同樣威脅. 在一些國家,對于用戶密碼存儲有著相關規(guī)定.
1. 密碼不以明文存儲
2. 密碼不以簡單hash方式存儲
hash方式包括hash算法選型瞻离、混淆手段及迭代次數。
算法選型
AES256是較安全的算法, MD5和SHA128屬于較簡單的算法, 一方面是因為它們的安全空間相對于如今的計算能力而言, 應對生日攻擊(Birthdate Attack)的安全性已經變弱. 另一方面, 它們存在的年代已久, 已經有了較大規(guī)模的hash數據庫, 可能通過簡單的數據庫查詢就可以找到對應明文
生日攻擊: 如果一個房間裡有23個或23個以上的人乒裆,那么至少有兩個人的生日相同的概率要大于50%
以MD5為例,理論上講它存在264中可能性,但其實只需嘗試232種可能就有很大可能找到碰撞,而借助一些方法還可以將嘗試次數降低到2^16以內
混淆手段
直接對明文,或者是簡單加上固定的一段字符串進行hash運算并存儲是不明智的選擇, 這帶來兩個問題:一方面, 攻擊者獲取到hash值后, 可以自行破解/查詢數據庫直接獲取到明文, 另一方面, 如果你的數據庫中有兩個用戶碰巧使用相同的密碼,那么攻擊者就會意識到這可能是一個弱密碼(如123456). 獲取到數據庫的攻擊者就很容易找到它,并進行集中破解.
比較可靠的辦法是對每個用戶使用一段隨機的字符串(通常稱為salt),在運算hash時將其與明文連接運算.
迭代次數
如果只進行一次hash,以MD5為例,它存在2^64中可能,某個數據庫中可能就存在著它的明文. 如果使用兩次,那么就進一步縮小了攻擊者直接通過hash找到明文的可能性. 當然,這并不能阻止攻擊者通過字典攻擊找出諸如"abcd1234"這樣的簡單密碼.
密碼傳輸
密碼傳輸存在于兩個階段: 注冊和認證.
1. 注冊
Django和很多網站在注冊期間密碼都是通過明文傳輸的, 某些網站甚至沒有使用HTTPS保護這些密碼明文.
防范措施:
- 由服務器發(fā)出一段salt, 客戶端將其與密碼明文運算后發(fā)送給服務器.
- 通過HTTPS保護注冊過程
服務端存儲 K = hash( password, reg_salt )
2. 認證
不論服務器上以和何種方式存儲了用戶密碼, 用戶和服務器之間總要通過一種方式進行驗證. 這中間存在幾種可能的攻擊:
重放攻擊: 攻擊者監(jiān)聽用戶發(fā)出的內容, 并將其用于自己的認證.
防范方式:由服務器發(fā)送給用戶一段隨機數值(salt), 由用戶運算后在服務端進行驗證. 由于這個值每次都不同,攻擊者無法將其用于自己的身份認證. 這種方式也被稱為challenge字典/窮舉攻擊: 攻擊者監(jiān)聽整個通訊過程,得知了此次的salt和用戶發(fā)送的hash值,自行進行窮舉/字典查詢. 對于某些簡單密碼, 例如純數字密碼, 這種窮舉只需要極少運算即可. 例如在我上大學期間, 學校網絡供應商提供的是一種6位數字密碼的上網卡, 其密碼驗證方式就是通過此種方式進行, 只需要根據salt,窮舉6位數字(10^6, 較差的電腦也只需要數秒), 即可獲得密碼明文.
防范方式: 通過HTTPS進行通訊加密,防止嗅探到明文.偽造服務器攻擊: 攻擊者謊稱自己是服務器,并向用戶發(fā)送一段有缺陷的salt. 假如混淆方法是字符串連接,那么攻擊者指定salt為空字符串, 就獲得了明文的直接hash值, 大大提高了攻擊成功幾率.
防范方式:通過HTTPS進行對服務端的身份認證套利,使用字符串補齊等更復雜的方式
認證過程:
- 服務端告知客戶端注冊時的鹽值及本次鹽值: reg_salt, challenge_salt , 并運算:
server_k = hash( K, challenge_salt ) - 客戶端進行運算,并發(fā)送給服務端
K = hash( password, reg_salt )
client_k = hash( K, challenge_salt ) - 服務端比較 client_k與server_k
重置密碼安全問題設計
現在比較流行的方法是使用手機或安全郵箱進行密碼重置. 某些還會提供一種通過安全問題進行重置.
考慮到密碼的兩種主要泄漏方式:偷看(shoulder surfing)和社會工程(social engineering), 查戶口式的"安全問題"并不安全. 例如這些問題:
- 你媽姓什么?
- 你畢業(yè)于哪個大學?
- 你喜歡的歌手叫什么名字?
- 你喜歡的運動是什么?
在某些情況下, 這些所謂的安全問題只需要簡單的調查就可以得到答案. 有趣的是, 早年的網易郵箱就曾經使用過最后一個問題"你喜歡的運動是什么", 然后我輕而易舉的差點重置了臨班某同學的郵箱密碼
安全問題設計上應當注意以下方面:
- 能夠通過某些文檔獲取的資料不應當用于安全問題
例如很多公司面試期間會要求填寫教育史,父母信息等, 這些問題不應當用于安全問題 - 用戶身邊的人能夠通過日常交往得到答案的, 不應成為安全問題
例如在工作場合, 用戶喜歡的食物/運動, 以及用戶的家鄉(xiāng)等,都是很容易打聽到的. 相對而言,初戀的姓氏就不是一個容易泄漏的話題 - 不應當設計某些隱私問題
一旦泄漏可能對用戶造成困擾. 較妥的辦法是以hash形式存儲這些問題的答案,并且問問題時不應當要求用戶回答"初戀的名字"這種問題, 更好的問題是"初戀的姓氏"或者"初戀的姓氏首字母" - 不使用固定的問題. 例如可以存儲6個問題的答案,每次隨機抽取三個, 增加攻擊者的打探難度.