面試官:設(shè)計(jì)一個(gè)安全的登錄都要考慮哪些?我一臉懵逼。。
點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)
作者:letcafe
來(lái)源:www.cnblogs.com/letcafe/p/loginSecurity.html
大家有沒(méi)有被問(wèn)過(guò)?反正我有一次是被問(wèn)的一臉懵逼。。
1. 一個(gè)簡(jiǎn)單的HTML例子看看用戶(hù)信息安全
標(biāo)準(zhǔn)的HTML語(yǔ)法中,支持在form表單中使用 <input></input> 標(biāo)簽來(lái)創(chuàng)建一個(gè)HTTP提交的屬性,現(xiàn)代的WEB登錄中。
<form action = "http://localhost:8080/Application/login" method = "POST">
用戶(hù)名:<input id="username" name="username" type="text" />
密碼:<input id="password" name="password" type="password" />
<button type="submit">登陸</button>
</form>

例如我的賬號(hào)是user1,密碼是123456,那么我在提交登錄的時(shí)候會(huì)給后臺(tái)發(fā)送的HTTP請(qǐng)求如下(Chrome或者FireFox開(kāi)發(fā)者工具捕獲,需開(kāi)啟Preserve log):

可以發(fā)現(xiàn)即便password字段是黑點(diǎn),但是本機(jī)仍以明文的形式截獲請(qǐng)求。
2. HTTP協(xié)議傳輸直接暴露用戶(hù)密碼字段
在網(wǎng)絡(luò)傳輸過(guò)程中,被嗅探到的話(huà)會(huì)直接危及用戶(hù)信息安全,以Fiddler或Wireshark為例,發(fā)現(xiàn)捕獲的HTTP報(bào)文中包含敏感信息:

3. 使用加密算法能保證密碼安全嗎?
WEB前端可以通過(guò)某種算法,對(duì)密碼字段進(jìn)行加密后,在將密碼作為Http請(qǐng)求的內(nèi)容進(jìn)行提交,常見(jiàn)的包括對(duì)稱(chēng)和非對(duì)稱(chēng)加密。
對(duì)稱(chēng)加密:采用對(duì)稱(chēng)密碼編碼技術(shù),它的特點(diǎn)是文件加密和解密使用相同的密鑰加密。另外,加密算法系列面試題和答案全部整理好了,微信搜索Java技術(shù)棧,在后臺(tái)發(fā)送:面試,可以在線(xiàn)閱讀。
非對(duì)稱(chēng)加密:需要兩個(gè)密鑰,公開(kāi)密鑰(publickey)和私有密鑰(privatekey)。公開(kāi)密鑰與私有密鑰是一對(duì),如果用公開(kāi)密鑰對(duì)數(shù)據(jù)進(jìn)行加密,只有用對(duì)應(yīng)的私有密鑰才能解密;如果用私有密鑰對(duì)數(shù)據(jù)進(jìn)行加密,那么只有用對(duì)應(yīng)的公開(kāi)密鑰才能解密。
3.1 使用對(duì)稱(chēng)加密
加密解密在前后臺(tái)協(xié)商后,似乎是個(gè)不錯(cuò)的辦法,比如,前臺(tái)使用一個(gè)字符串位移+字符串反轉(zhuǎn)的簡(jiǎn)單方法(舉個(gè)例子,當(dāng)然不能這么簡(jiǎn)單)。那么,如果原密碼123456先移位:
123456-->456123
再進(jìn)行反轉(zhuǎn):
456123-->321654
那么這樣簡(jiǎn)單的方法似乎可以混淆原密碼,并且輕松由后臺(tái)進(jìn)行相反操作復(fù)原。但是這有兩個(gè)缺點(diǎn):
前后端加密解密需要同時(shí)修改代碼; 前端加密無(wú)非是寫(xiě)在JS里,但是JS有風(fēng)險(xiǎn)被直接破解從而識(shí)別加密方法。
3.2 非對(duì)稱(chēng)加密HTTPS就一定是安全的嗎?
非對(duì)稱(chēng)加密有著公鑰私鑰的存在,公鑰可以隨意獲取,私鑰是用來(lái)對(duì)公鑰解密的本地存儲(chǔ),通過(guò)公私鑰的機(jī)制似乎可以保證傳輸加密并且乃至現(xiàn)在還在使用的HTTPS就是基于這個(gè)原理。但是HTTPS就一定安全嗎?HTTP存在兩種可能的風(fēng)險(xiǎn):
HTTPS可以保證傳輸過(guò)程中的信息不被別人截獲,但是細(xì)細(xì)思考下,HTTPS是應(yīng)用層協(xié)議,下層采用SSL保證信息安全,但是在客戶(hù)端和服務(wù)端,密文同樣是可以被截獲的; HTTPS報(bào)文在傳輸過(guò)程中,如果客戶(hù)端被惡意引導(dǎo)安裝“中間人”的WEB信任證書(shū),那么HTTPS中的“中間人攻擊”一樣會(huì)將明文密碼泄露給別人。
4. 結(jié)論是,無(wú)論HTTP還是HTTPS,密碼必須密文傳輸
想想HTTPS也不能一定保障用戶(hù)密碼信息,那么就應(yīng)該考慮在應(yīng)用層之上再繼續(xù)對(duì)密碼進(jìn)行保護(hù),也就是編寫(xiě)代碼來(lái)進(jìn)行控制,而不依賴(lài)特定協(xié)議,比較容易想到的就是利用不可逆加密散列函數(shù)MD5(string),用戶(hù)在注冊(cè)輸入密碼的時(shí)候,就存儲(chǔ)MD5(password)值,并且在WEB端先進(jìn)行MD5(password),然后將密碼傳輸至后臺(tái),與數(shù)據(jù)庫(kù)中的密文進(jìn)行比較(PS:MD5函數(shù)在指定位數(shù)的情況下,對(duì)相同字符串運(yùn)算值相同)。優(yōu)點(diǎn)比較明顯:
保證了用戶(hù)數(shù)據(jù)庫(kù)內(nèi)部的密碼信息安全; 傳輸過(guò)程中無(wú)論如何都不會(huì)使得用戶(hù)的密文被破解出原密碼; 簡(jiǎn)單高效,執(zhí)行以及編碼難度都不大,各種語(yǔ)言都提供MD5支持,開(kāi)發(fā)快。
5. 那太好了!這樣可以省下HTTPS的錢(qián)了,真是這樣嗎?
回到開(kāi)頭的例子:用戶(hù)輸入的用戶(hù)名是:user1,密碼是:123456,那么不管在什么協(xié)議之下,可以看到實(shí)際發(fā)送的HTTP/HTTPS報(bào)文在MD5處理后是這樣的:

沒(méi)錯(cuò),加密登錄成功了。但是,當(dāng)我們慶祝密碼安全的時(shí)候,發(fā)現(xiàn)賬戶(hù)的錢(qián)突然不翼而飛。
這是為什么呢?黑客卻笑的很開(kāi)心:因?yàn)樗麄儾⒉灰欢ㄒ@取到你的密碼明文,如果直接截獲你的密碼密文,然后發(fā)送給服務(wù)器不是一樣可以登錄嗎?因?yàn)閿?shù)據(jù)庫(kù)里的不也是MD5(password)的一樣的密文嗎?HTTP請(qǐng)求被偽造,一樣可以登錄成功,從而攫取其他的數(shù)據(jù)或者轉(zhuǎn)走余額。
這怎么辦?其實(shí)并不難,有很多種解決方法?其實(shí)原理都是類(lèi)似的:那就是服務(wù)器緩存生成隨機(jī)的驗(yàn)證字段,并發(fā)送給客戶(hù)端,當(dāng)客戶(hù)端登錄時(shí),把這個(gè)一并字段傳給服務(wù)器,用于校驗(yàn)。
另外,最新 Java 核心技術(shù)教程和示例源碼看這里:https://github.com/javastacks/javastack
5.1 方案一:驗(yàn)證碼
MVC場(chǎng)景。控制器將把數(shù)據(jù)的Model封裝到View中,這種存在Session的連接方式,允許了在Session中存取信息。那么我們可以利用一些開(kāi)源的驗(yàn)證碼生成工具,例如JAVA中的Kaptcha,在服務(wù)端存放生成一個(gè)驗(yàn)證碼值以及一個(gè)驗(yàn)證碼的生成圖片,將圖片以Base64編碼,并返回給View,在View中解碼Base64并加載圖片,并于用戶(hù)下次登錄時(shí)再進(jìn)行比對(duì)。
5.2 方案二:token令牌
前后端分離場(chǎng)景?,F(xiàn)在非常流行的前后端分離的開(kāi)發(fā)模式大大提高了項(xiàng)目的開(kāi)發(fā)效率。職責(zé)、分工明確,但是由于HTTP是無(wú)狀態(tài)的(就是這一次請(qǐng)求并不知道上一次請(qǐng)求的內(nèi)容),當(dāng)用戶(hù)登錄時(shí),根據(jù)用戶(hù)的username作為key,生成隨機(jī)令牌(例如UUID)作為value緩存在Redis中,并且將token返回給客戶(hù)端,當(dāng)客戶(hù)端登錄時(shí),將完成校驗(yàn),并且刪除Redis中的那條緩存記錄。
那么每次從服務(wù)器中獲取認(rèn)證的token,確實(shí)能保證HTTP請(qǐng)求是由前端傳回來(lái)的了,因?yàn)閠oken在每次登陸后都會(huì)刪除并被重置,會(huì)導(dǎo)致黑客嘗試重放賬號(hào)密碼數(shù)據(jù)信息來(lái)登陸的時(shí)候?qū)е聼o(wú)法成功登陸。
總而言之,就是我拿到了賬號(hào)以及密碼的密文也登陸不了,因?yàn)?,如果?qǐng)求不包含后臺(tái)認(rèn)證的令牌token,是個(gè)非法請(qǐng)求。
點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)
6. 太不容易了!可是還別高興的太早,當(dāng)心數(shù)據(jù)被篡改
密碼也加密了,黑客看不到明文了。加上Token了,登陸過(guò)程也沒(méi)法再被截獲重放了??墒窍胂脒@種情況,你在進(jìn)行某寶上的網(wǎng)絡(luò)支付,需要賬號(hào),密碼,金額,token這四個(gè)字段進(jìn)行操作,然后支付的時(shí)候你付了1塊錢(qián)買(mǎi)了一袋包郵的小浣熊干脆面,某寶結(jié)算結(jié)束后,你發(fā)現(xiàn)你的賬戶(hù)余額被扣了1萬(wàn)元。這又是怎么回事呢?
因?yàn)榧幢愫诳筒坏卿洠徊僮?,一樣要搞破壞:?dāng)請(qǐng)求路由到黑客這邊的時(shí)候,截獲數(shù)據(jù)包,然后也不需要登錄,反正賬號(hào)密碼都是對(duì)的,token也是對(duì)的,那么把數(shù)據(jù)包的字段改改,搞破壞就可以了,于是把money改成了1萬(wàn),再傳給服務(wù)器,作為受害者就莫名其妙踩了這個(gè)坑??蛇@該怎么解決呢?其實(shí)原理類(lèi)似于HTTPS里的數(shù)字簽名機(jī)制,首先科普下什么是數(shù)字摘要以及數(shù)字簽名:
6.1 什么是“數(shù)字摘要”
我們?cè)谙螺d文件的時(shí)候經(jīng)常會(huì)看到有的下載站點(diǎn)也提供下載文件的“數(shù)字摘要“,供下載者驗(yàn)證下載后的文件是否完整,或者說(shuō)是否和服務(wù)器上的文件”一模一樣“。
其實(shí),數(shù)字摘要就是采用單項(xiàng)Hash函數(shù)將需要加密的明文“摘要”成一串固定長(zhǎng)度(128位)的密文,這一串密文又稱(chēng)為數(shù)字指紋,它有固定的長(zhǎng)度,而且不同的明文摘要成密文,其結(jié)果總是不同的,而同樣的內(nèi)容信息其摘要必定一致。
因此,“數(shù)字摘要“叫”數(shù)字指紋“可能會(huì)更貼切一些。“數(shù)字摘要“是HTTPS能確保數(shù)據(jù)完整性和防篡改的根本原因。
6.2 數(shù)字簽名--水到渠成的技術(shù)
假如發(fā)送方想把一份報(bào)文發(fā)送給接收方,在發(fā)送報(bào)文前,發(fā)送方用一個(gè)哈希函數(shù)從報(bào)文文本中生成報(bào)文摘要,然后用自己的私人密鑰對(duì)這個(gè)摘要進(jìn)行加密,這個(gè)加密后的摘要將作為報(bào)文的”簽名“和報(bào)文一起發(fā)送給接收方。
接收方首先用與發(fā)送方一樣的哈希函數(shù)從接收到的原始報(bào)文中計(jì)算出報(bào)文摘要,接著再用發(fā)送方的公用密鑰來(lái)對(duì)報(bào)文附加的數(shù)字簽名進(jìn)行解密,如果這兩個(gè)摘要相同、那么接收方就能確認(rèn)報(bào)文是從發(fā)送方發(fā)送且沒(méi)有被遺漏和修改過(guò)!
這就是結(jié)合“非對(duì)稱(chēng)密鑰加解密”和“數(shù)字摘要“技術(shù)所能做的事情,這也就是人們所說(shuō)的“數(shù)字簽名”技術(shù)。在這個(gè)過(guò)程中,對(duì)傳送數(shù)據(jù)生成摘要并使用私鑰進(jìn)行加密地過(guò)程就是生成”數(shù)字簽名“的過(guò)程,經(jīng)過(guò)加密的數(shù)字摘要,就是”數(shù)字簽名“。
因此,我們可以在WEB端對(duì)之前案例中提到的username+MD5(password)+token通過(guò)簽名,得到一個(gè)字段checkCode,并將checkCode發(fā)送給服務(wù)器,服務(wù)器根據(jù)用戶(hù)發(fā)送的checkCode以及自身對(duì)原始數(shù)據(jù)簽名進(jìn)行運(yùn)算比對(duì),從而確認(rèn)數(shù)據(jù)是否中途被篡改,以保持?jǐn)?shù)據(jù)的完整性。
7. 總結(jié)
看似非常簡(jiǎn)單的WEB登錄,其實(shí)里面也存在著非常多的安全隱患。這些安全完善的過(guò)程是在一個(gè)實(shí)際WEB項(xiàng)目中遇到的,上面的分析演化是在應(yīng)對(duì)項(xiàng)目安全的檢查中所提出的解決方案,多少會(huì)有很多不足的地方,希望一起交流探討,共同進(jìn)步!
補(bǔ)充1:JS加密函數(shù)存在被破解
感謝園友mysgk指出完整性檢驗(yàn)中關(guān)于JS加密函數(shù)存在被破解的問(wèn)題:
問(wèn)題描述:
如果黑客通過(guò)閱讀前端js源碼,發(fā)現(xiàn)加密算法,是否意味他可以構(gòu)造可以 被服務(wù)端解密的checkCode 來(lái)欺騙服務(wù)端呢 ?
我想了下,應(yīng)該也是很多網(wǎng)站也在采取的策略:
摘要或加密JS算法不直接以靜態(tài)文件的形式存在瀏覽器中,而是讓W(xué)EB端去請(qǐng)求Server,服務(wù)器可以根據(jù)隨機(jī)令牌token值決定返回一個(gè)相應(yīng)隨機(jī)的加密策略,以JS代碼響應(yīng)的方式返回,在異步請(qǐng)求響應(yīng)中,加載JS摘要算法,這樣客戶(hù)端就可以動(dòng)態(tài)加載數(shù)字摘要策略,保證無(wú)法仿造。
補(bǔ)充2:MD5存在隱患的問(wèn)題
感謝園友EtherDream提出MD5已經(jīng)過(guò)時(shí),并且存在不安全的問(wèn)題:
問(wèn)題描述:
用MD5、SHA256 處理密碼的過(guò)時(shí)了。。?,F(xiàn)在 PBKDF、bcrypt 都在過(guò)時(shí)中。
本文重點(diǎn)側(cè)重于方法思路的介紹,并不一定是要使用MD5函數(shù),可以使用其他的方式。 MD5存在隱患,之前確實(shí)沒(méi)有考慮太多,不過(guò)非常感謝園友指出,確實(shí)是這樣的,主要思想是:
對(duì)于MD5的破解,實(shí)際上都屬于【碰撞】。比如原文A通過(guò)MD5可以生成摘要M,我們并不需要把M還原成A,只需要找到原文B,生成同樣的摘要M即可。設(shè)MD5的哈希函數(shù)是MD5(),那么:MD5(A) = M MD5(B) = M 任意一個(gè)B即為破解結(jié)果。B有可能等于A,也可能不等于A。
大概意思也就是,截獲了MD5加密后的密文,一樣可以,找到一個(gè)不是原密碼,但是加密后可以登陸成功的“偽原文”。
從中可以看到一點(diǎn),MD5函數(shù)確實(shí)能被反向“破解”,但是這個(gè)“破解”只是找到一個(gè)經(jīng)過(guò)MD5運(yùn)算后得到相同結(jié)果的原文,并非是用戶(hù)的明文密碼。但是這樣會(huì)被破解登錄的可能,確實(shí)是需要采用更完善的算法進(jìn)行加密,再次感謝!






關(guān)注Java技術(shù)??锤喔韶?/strong>


