移動App該如何保存用戶密碼?
這個實際上和桌面程序是一樣的。
先看下一些軟件是如何保存用戶密碼的:
我們先來看下QQ是怎麼保存密碼的:
參考:http://bbs.pediy.com/archive/index.php?t-159045.html,
桌面QQ在2012的時候把密碼md5計算之後,保存到本地加密的Sqlite數據庫裏。
再來看下手機淘寶是怎麼做的:
參考:http://blog.csdn.net/androidsecurity/article/details/8666954
手機淘寶是通過本地DES加密,再把密碼保存到本地文件裏的,如果拿到ROOT權限,能破解出密碼明文。
再來看下微軟是怎麼保存用戶密碼的:
參考:http://www.freebuf.com/tools/37162.html
我實際測試了下,可以輕鬆得到所有帳號的密碼明文。
再來看下Linux是怎麼保存用戶密碼的:
參考:http://blog.csdn.net/lqhbupt/article/details/7787802
linux是通過加鹽(salt),再hash後,保存到/etc/shadow文件裏的。
貌似以前的發行版是md5 hash,現在的發行版都是SHA-512 hash。
linux用戶密碼的hash算法: http://serverfault.com/questions/439650/how-are-the-hashes-in-etc-shadow-generated
實際上是調用了glic裏的crypt函數,可以在man手冊裏查看相關的信息。
可以用下面的命令來生成:
mkpasswd --method=SHA-512 --salt=xxxx
其中salt參數,可以自己設置,最好是隨機生成的。
可以用 mkpasswd --method=help 來查看支持的算法。
用戶密碼該如何保存,還有能做到哪種程度?
看完上面一些軟件的做法之後,我們來探討下,用戶密碼該如何保存,還有能做到哪種程度?
假定本地存儲的hash串/加密串,和加密算法,***者都可以得到,或者逆向分析到。
實際上也是如此,通過上面QQ和淘寶的例子,允分說明了加密串是可以得到的。Linux更是一切都是公開的,只要有權限就可以讀取到,包括salt值,shah算法,(salt+密碼) hash之後的結果。
防止***者得到用戶密碼的明文。這個實際上是從用戶的角度出發,即使數據泄露了,影響降到最低。
防止***者拿到hash串或者加密串之後,一直都可以登陸。這點對於移動設置是很重要的,比如今天用戶連到了一個惡意的wifi,如果***者截獲到請求,要防止***者潛伏几天,或者幾個月之後的***。必須要讓請求的憑據在一天或者幾天內失效。
加鹽(salt)
假如不加鹽,那麼***者可以根據同樣的hash值得到很多信息。
比如網站1的數據庫泄露了,***者發現用戶A和用戶B的hash值是一樣的,然後***者通過其它途徑拿到了用戶A的密碼,那麼***者就可以知道用戶B的密碼了。
或者***者通過彩虹表,暴力破解等方式可以直接知道用戶的原來密碼。
所以,每個用戶的salt值都要是不一樣的,這點參考linux的/etc/shadow文件就知道了。
客戶端本地存儲密碼的算法
應該用哪種算法來存儲?
從上面的資料來看,手機淘寶是本地DES對稱加密,顯然很容易就可以破解到用戶的真實密碼。QQ也是對稱加密的數據庫裏,存儲了用戶密碼的md5值。
顯然對稱加密算法都是可以逆向得到原來的數據的。那麼我們嘗試用非對稱加密算法,比如RSA來傳輸用戶的密碼。
那麼用戶登陸的流程就變爲:
客戶端用公鑰加密用戶密碼,保存到本地;
用戶要登陸時,發送加密串到服務器;
服務器用私鑰解密,得到用戶的密碼,再驗證。
有的人會說,如果服務器的私鑰泄露怎麼辦?
服務器端換個新的密鑰,強制客戶端下載新的公鑰或者升級。
可以考慮有一個專門的硬件來解密,這個硬件只負責計算,私鑰是一次性寫入不可讀取和修改的。搜索 rsa hardware,貌似的確有這樣的硬件。
當然,即使真的私鑰泄露,世界一樣運轉,像OpenSSL的心血漏洞就可能泄露服務器私鑰,但大家日子一樣過。
非對稱加密算法的好處:
即使數據被盜,***者拿不到密碼的明文
如果發現有部分用戶的數據被盜了(公鑰加密後的數據),可以通過升級服務器和客戶端的版本,讓用戶重新輸入密碼,用戶還是原來的密碼,但是***者卻登陸不了了。
對於安全要求嚴格的應用,還可以定期更新私鑰,來保證用戶的數據安全。
如何防止本地加密串泄露之後,***者可能潛伏很久?
這點實際上是如何讓客戶端保存的加密串及時的失效。
比如:
強制要求客戶端保存的加密串一週失效;
用戶手機中病毒了,***者竊取到了加密串。但是清除病毒之後,用戶沒有夠時的修改密碼。***者是否會潛伏很久?
發現某***大規模竊取到了大量的用戶本地加密串,是否可以強制用戶的本地加密串失效,客戶端不用升級,用戶不用修改密碼,也不會泄露信息?
下面提出一種 salt + 非對稱加密算法的方案來解決這個問題:
用戶填寫密碼,客戶端隨機生成一個salt值(注意這個salt只是防止中間人攔截到原始的password的加密串),用公鑰把 (salt + password)加密,設置首次登陸的參數,發送到服務器;
服務器檢查參數,發現是首次登陸,則服務器用私鑰解密,得到password(拋棄salt值),驗證,如果通過,則隨機生成一個salt值,並把salt值保存起來(保存到緩存裏,設置7天過期),然後用公鑰把(salt + 用戶名)加密,返回給客戶端。
客戶端保存服務器返回的加密串,完成登陸。
客戶端下次自動登陸時,把上次保存的加密串直接發給服務器,並設置二次登陸的參數。
服務器檢查參數,發現是二次登陸,用私鑰解密,得到salt + 用戶名,然後檢查salt值是否過期了(到緩存中查找,如果沒有,即過期),如果過期,則通知客戶端,讓用戶重新輸入密碼。如果沒有過期,再驗證密碼是否正確。如果正確,則通知客戶端登陸成功。
如果發現某帳戶異常,可以直接清除緩存中對應用戶的salt值,這樣用戶再登陸就會失敗。同理,如果某***大規模竊取到了大量的用戶本地加密串,那麼可以把緩存中所有用戶的salt都清除,那麼所有用戶都要重新登陸。注意用戶的密碼不用修改。
第2步中服務器生成的salt值,可以帶上用戶的mac值,os版本等,這樣可以增強檢驗。
注意,爲了簡化描述,上面提到的用戶的password,可以是先用某個hash算法hash一次。
具體的登陸流程:
瀏覽器登陸的流程:
瀏覽器的登陸過程比較簡單,只要用RSA公鑰加密密碼就可以了。防止中間人截取到明文的密碼。
App登陸保存數據流程
App因爲要實現自動登陸功能,所以必然要保存一些憑據,所以比較複雜。
App登陸要實現的功能:
密碼不會明文存儲,並且不能反編繹解密;
在服務器端可以控制App端的登陸有效性,防止***者拿到數據之後,可以長久地登陸;
用戶如果密碼沒有泄露,不用修改密碼就可以保證安全性;
可以區分不同類型的客戶端安全性;比如Android用戶受到***,只會讓Android用戶的登陸失效,IOS用戶不受影響。
App第一次登陸流程:
用戶輸入密碼,App把這些信息用RSA公鑰加密:(用戶名,密碼,時間,mac,隨機數),併發送到服務器。
服務器用RSA私鑰解密,判斷時間(可以動態調整1天到7天),如果不在時間範圍之內,則登陸失敗。如果在時間範圍之內,再調用coreservice判斷用戶名和密碼。
這裏判斷時間,主要是防止***者截取到加密串後,可以長久地利用這個加密串來登陸。
如果服務器判斷用戶成功登陸,則用AES加密:(隨機salt,用戶名,客戶端類型,時間),以(用戶名+Android/IOS/WP)爲key,存到緩存裏。再把加密結果返回給客戶端。
客戶端保存服務器返回的加密串
App自動登陸的流程:
App發送保存的加密串到服務器,(加密串,用戶名,mac,隨機數)==>RSA公鑰加密
服務器用RSA私鑰解密,再用AES解密加密串,判斷用戶名是否一致。如果一致,再以(用戶名+Android/IOS/WP)爲key到緩存裏查詢。如果判斷緩存中的salt值和客戶端發送過來的一致,則用戶登陸成功。否則登陸失敗。
不用AES加密,用RSA公鑰加密也是可以的。AES速度比RSA要快,RSA只能存儲有限的數據。
其它的一些東東:
多次md5或者md5 + sha1是沒什麼效果的。
RSA算法最好選擇2048位的。搜索" rsa 1024 crack"有很多相關的結果,google已經將其SSL用的RSA算法升級爲2048位的。
如何防止登陸過程的中間人***,可以參考,魔獸世界的叫SPR6的登陸算法。
總結:
對於網頁登陸,可以考慮支持多種方式:
不支持JS的,用原始密碼登陸。
支持JS的,可以考慮傳遞hash算法加密字符串。嚴格要求的應用,最好用JS實現RSA加密。在github上找到的一個JS RSA庫:https://github.com/travist/jsencrypt
客戶端應用,一律應當用RSA算法,並加鹽來保存用戶密碼。單純的hash或者對稱加密算法都不靠譜。
服務器用salt(存數據庫的) + hash算法來保存用戶的密碼。
用salt(存緩存的,注意和上一行的salt是不同的)+ RSA算法來加密用戶登陸的憑證。
這樣服務器可以靈活控制風險,控制用戶登陸憑據的有效期,即使用戶數據泄露,也不需要修改密碼。