Web登入其實沒那麼簡單
1. 一個簡單的HTML例子看看使用者資訊保安
標準的HTML語法中,支援在form表單中使用<input></input>標籤來建立一個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以及自身對原始資料簽名進行運算比對,從而確認資料是否中途被篡改,以保持資料的完整性。