天天看點

面試官:設計一個安全的登入都要考慮哪些?我一臉懵逼。。

1. 一個簡單的HTML例子看看使用者資訊安全

标準的HTML文法中,支援在form表單中使用

标簽來建立一個HTTP送出的屬性,現代的WEB登入中,常見的是下面這樣的表單:

<form action = "http://localhost:8080/Application/login" method = "POST">
    使用者名:<input id="username" name="username" type="text" />
    密碼:<input id="password" name="password" type="password" />
    <button type="submit">登陸</button>
</form>      

form表單會在送出請求時,會擷取form中input标簽存在name的屬性,作為HTTP請求的body中的參數傳遞給背景,進行登入校驗。

面試官:設計一個安全的登入都要考慮哪些?我一臉懵逼。。

例如我的賬号是user1,密碼是123456,那麼我在送出登入的時候會給背景發送的HTTP請求如下(Chrome或者FireFox開發者工具捕獲,需開啟Preserve log):

面試官:設計一個安全的登入都要考慮哪些?我一臉懵逼。。

可以發現即便password字段是黑點,但是本機仍以明文的形式截獲請求。

2. HTTP協定傳輸直接暴露使用者密碼字段

在網絡傳輸過程中,被嗅探到的話會直接危及使用者資訊安全,以Fiddler或Wireshark為例,發現捕獲的HTTP封包中包含敏感資訊:

面試官:設計一個安全的登入都要考慮哪些?我一臉懵逼。。

3. 使用加密算法能保證密碼安全嗎?

WEB前端可以通過某種算法,對密碼字段進行加密後,在将密碼作為Http請求的内容進行送出,常見的包括對稱和非對稱加密。

對稱加密:采用對稱密碼編碼技術,它的特點是檔案加密和解密使用相同的密鑰加密。

非對稱加密:需要兩個密鑰,公開密鑰(publickey)和私有密鑰(privatekey)。公開密鑰與私有密鑰是一對,如果用公開密鑰對資料進行加密,隻有用對應的私有密鑰才能解密;如果用私有密鑰對資料進行加密,那麼隻有用對應的公開密鑰才能解密。

3.1 使用對稱加密

加密解密在前背景協商後,似乎是個不錯的辦法,比如,前台使用一個字元串位移+字元串反轉的簡單方法(舉個例子,當然不能這麼簡單)。那麼,如果原密碼123456先移位:

123456-->456123      

再進行反轉:

456123-->321654      

那麼這樣簡單的方法似乎可以混淆原密碼,并且輕松由背景進行相反操作複原。但是這有兩個缺點:

前後端加密解密需要同時修改代碼;

前端加密無非是寫在JS裡,但是JS有風險被直接破解進而識别加密方法。

3.2 非對稱加密HTTPS就一定是安全的嗎?

非對稱加密有着公鑰私鑰的存在,公鑰可以随意擷取,私鑰是用來對公鑰解密的本地存儲,通過公私鑰的機制似乎可以保證傳輸加密并且乃至現在還在使用的HTTPS就是基于這個原理。 但是HTTPS就一定安全嗎?HTTP存在兩種可能的風險:

HTTPS可以保證傳輸過程中的資訊不被别人截獲,但是細細思考下,HTTPS是應用層協定,下層采用SSL保證資訊安全,但是在用戶端和服務端,密文同樣是可以被截獲的;

HTTPS封包在傳輸過程中,如果用戶端被惡意引導安裝“中間人”的WEB信任證書,那麼HTTPS中的“中間人攻擊”一樣會将明文密碼洩露給别人。

4. 結論是,無論HTTP還是HTTPS,密碼必須密文傳輸

想想HTTPS也不能一定保障使用者密碼資訊,那麼就應該考慮在應用層之上再繼續對密碼進行保護,也就是編寫代碼來進行控制,而不依賴特定協定,比較容易想到的就是利用不可逆加密散列函數MD5(string),使用者在注冊輸入密碼的時候,就存儲MD5(password)值,并且在WEB端先進行MD5(password),然後将密碼傳輸至背景,與資料庫中的密文進行比較(PS:MD5函數在指定位數的情況下,對相同字元串運算值相同)。優點比較明顯:

保證了使用者資料庫内部的密碼資訊安全;

傳輸過程中無論如何都不會使得使用者的密文被破解出原密碼;

簡單高效,執行以及編碼難度都不大,各種語言都提供MD5支援,開發快。

5. 那太好了!這樣可以省下HTTPS的錢了,真是這樣嗎?

回到開頭的例子:使用者輸入的使用者名是:user1,密碼是:123456,那麼不管在什麼協定之下,可以看到實際發送的HTTP/HTTPS封包在MD5處理後是這樣的:

面試官:設計一個安全的登入都要考慮哪些?我一臉懵逼。。

沒錯,加密登入成功了。但是,當我們慶祝密碼安全的時候,發現賬戶的錢突然不翼而飛。這是為什麼呢?黑客卻笑的很開心:因為他們并不一定要擷取到你的密碼明文,如果直接截獲你的密碼密文,然後發送給伺服器不是一樣可以登入嗎?因為資料庫裡的不也是MD5(password)的一樣的密文嗎?HTTP請求被僞造,一樣可以登入成功,進而攫取其他的資料或者轉走餘額。

這怎麼辦?其實并不難,有很多種解決方法?其實原理都是類似的:那就是伺服器緩存生成随機的驗證字段,并發送給用戶端,當用戶端登入時,把這個一并字段傳給伺服器,用于校驗。

5.1 方案一:驗證碼

MVC場景。控制器将把資料的Model封裝到View中,這種存在Session的連接配接方式,允許了在Session中存取資訊。那麼我們可以利用一些開源的驗證碼生成工具,例如JAVA中的Kaptcha,在服務端存放生成一個驗證碼值以及一個驗證碼的生成圖檔,将圖檔以Base64編碼,并傳回給View,在View中解碼Base64并加載圖檔,并于使用者下次登入時再進行比對。

5.2 方案二:token令牌

前後端分離場景。現在非常流行的前後端分離的開發模式大大提高了項目的開發效率。職責、分工明确,但是由于HTTP是無狀态的(就是這一次請求并不知道上一次請求的内容),當使用者登入時,根據使用者的username作為key,生成随機令牌(例如UUID)作為value緩存在Redis中,并且将token傳回給用戶端,當用戶端登入時,将完成校驗,并且删除Redis中的那條緩存記錄。

那麼每次從伺服器中擷取認證的token,确實能保證HTTP請求是由前端傳回來的了,因為token在每次登陸後都會删除并被重置,會導緻黑客嘗試重放賬号密碼資料資訊來登陸的時候導緻無法成功登陸。

總而言之,就是我拿到了賬号以及密碼的密文也登陸不了,因為,如果請求不包含背景認證的令牌token,是個非法請求。

6. 太不容易了!可是還别高興的太早,當心資料被篡改

密碼也加密了,黑客看不到明文了。加上Token了,登陸過程也沒法再被截獲重放了。可是想想這種情況,你在進行某寶上的網絡支付,需要賬号,密碼,金額,token這四個字段進行操作,然後支付的時候你付了1塊錢買了一袋包郵的小浣熊幹脆面,某寶結算結束後,你發現你的賬戶餘額被扣了1萬元。這又是怎麼回事呢?

因為即便黑客不登入,不操作,一樣要搞破壞:當請求路由到黑客這邊的時候,截獲資料包,然後也不需要登入,反正賬号密碼都是對的,token也是對的,那麼把資料包的字段改改,搞破壞就可以了,于是把money改成了1萬,再傳給伺服器,作為受害者就莫名其妙踩了這個坑。可這該怎麼解決呢?其實原理類似于HTTPS裡的數字簽名機制,首先科普下什麼是數字摘要以及數字簽名:

6.1 什麼是“數字摘要”

我們在下載下傳檔案的時候經常會看到有的下載下傳站點也提供下載下傳檔案的“數字摘要“,供下載下傳者驗證下載下傳後的檔案是否完整,或者說是否和伺服器上的檔案”一模一樣“。其實,數字摘要就是采用單項Hash函數将需要加密的明文“摘要”成一串固定長度(128位)的密文,這一串密文又稱為數字指紋,它有固定的長度,而且不同的明文摘要成密文,其結果總是不同的,而同樣的内容資訊其摘要必定一緻。

是以,“數字摘要“叫”數字指紋“可能會更貼切一些。“數字摘要“是HTTPS能確定資料完整性和防篡改的根本原因。

6.2 數字簽名--水到渠成的技術

假如發送方想把一份封包發送給接收方,在發送封包前,發送方用一個哈希函數從封包文本中生成封包摘要,然後用自己的私人密鑰對這個摘要進行加密,這個加密後的摘要将作為封包的”簽名“和封包一起發送給接收方,接收方首先用與發送方一樣的哈希函數從接收到的原始封包中計算出封包摘要,接着再用發送方的公用密鑰來對封包附加的數字簽名進行解密,如果這兩個摘要相同、那麼接收方就能确認封包是從發送方發送且沒有被遺漏和修改過!這就是結合“非對稱密鑰加解密”和“數字摘要“技術所能做的事情,這也就是人們所說的“數字簽名”技術。在這個過程中,對傳送資料生成摘要并使用私鑰進行加密地過程就是生成”數字簽名“的過程,經過加密的數字摘要,就是”數字簽名“。

是以,我們可以在WEB端對之前案例中提到的username+MD5(password)+token通過簽名,得到一個字段checkCode,并将checkCode發送給伺服器,伺服器根據使用者發送的checkCode以及自身對原始資料簽名進行運算比對,進而确認資料是否中途被篡改,以保持資料的完整性。

7. 總結

看似非常簡單的WEB登入,其實裡面也存在着非常多的安全隐患。這些安全完善的過程是在一個實際WEB項目中遇到的,上面的分析演化是在應對項目安全的檢查中所提出的解決方案,多少會有很多不足的地方,希望一起交流探讨,共同進步!

補充1:JS加密函數存在被破解

感謝園友mysgk指出完整性檢驗中關于JS加密函數存在被破解的問題:

問題描述:

如果黑客通過閱讀前端js源碼,發現加密算法,是否意味他可以構造可以 被服務端解密的checkCode 來欺騙服務端呢 ?

我想了下,應該也是很多網站也在采取的政策:

摘要或加密JS算法不直接以靜态檔案的形式存在浏覽器中,而是讓WEB端去請求Server,伺服器可以根據随機令牌token值決定傳回一個相應随機的加密政策,以JS代碼響應的方式傳回,在異步請求響應中,加載JS摘要算法,這樣用戶端就可以動态加載數字摘要政策,保證無法仿造。

補充2:MD5存在隐患的問題

感謝園友EtherDream提出MD5已經過時,并且存在不安全的問題:

用MD5、SHA256 處理密碼的過時了。。。現在 PBKDF、bcrypt 都在過時中。

本文重點側重于方法思路的介紹,并不一定是要使用MD5函數,可以使用其他的方式。

MD5存在隐患,之前确實沒有考慮太多,不過非常感謝園友指出,确實是這樣的,主要思想是:

對于MD5的破解,實際上都屬于【碰撞】。比如原文A通過MD5可以生成摘要M,我們并不需要把M還原成A,隻需要找到原文B,生成同樣的摘要M即可。 設MD5的哈希函數是MD5(),那麼: MD5(A) = M MD5(B) = M 任意一個B即為破解結果。 B有可能等于A,也可能不等于A。

大概意思也就是,截獲了MD5加密後的密文,一樣可以,找到一個不是原密碼,但是加密後可以登陸成功的“僞原文”。

從中可以看到一點,MD5函數确實能被反向“破解”,但是這個“破解”隻是找到一個經過MD5運算後得到相同結果的原文,并非是使用者的明文密碼。但是這樣會被破解登入的可能,确實是需要采用更完善的算法進行加密,再次感謝!