Web 登錄很簡單?開玩笑!

本文通過 Web 登錄的例子探討安全問題,登錄不僅僅是簡單地表達提交和記錄寫入,其安全問題纔是重中之重。

1. 一個簡單的 HTML 例子看看用戶信息安全

標準的 HTML 語法中,支持在 form 表單中使用 標籤來創建一個 HTTP 提交的屬性,現代的 WEB 登錄中,常見的是下面這樣的表單:

<form action= "Application/login" method = "POST">
   用戶名:<input id="username" />
   密碼:<input id="password"  />
   <button type="submit">登陸</button>
</form>

form 表單會在提交請求時, 會獲取 form 中 input 標籤存在 name 的屬性,作爲 HTTP 請求的 body 中的參數傳遞給後臺,進行登錄校驗。

例如我的賬號是 user1,密碼是 123456,那麼我在提交登錄的時候會給後臺發送的 HTTP 請求如下(Chrome 或者 FireFox 開發者工具捕獲,需開啓 Preserve log):

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

  1. HTTP 協議傳輸直接暴露用戶密碼字段

在網絡傳輸過程中,被嗅探到的話會直接危及用戶信息安全,以 Fiddler 或 Wireshark 爲例,發現捕獲的 HTTP 報文中包含敏感信息

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

WEB 前端可以通過某種算法,對密碼字段進行加密後,在將密碼作爲 Http 請求的內容進行提交,常見的包括對稱和非對稱加密。

對稱加密: 採用對稱密碼編碼技術,它的特點是文件加密和解密使用相同的密鑰加密。

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

3.1 使用對稱加密

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

123456-->456123

再進行反轉:

456123-->321654

那麼這樣簡單的方法似乎可以混淆原密碼,並且輕鬆由後臺進行相反操作復原。但是這有兩個缺點:

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

  2. 前端加密無非是寫在 JS 裏,但是 JS 有風險被直接破解從而識別加密方法。

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

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

1.HTTPS 可以保證傳輸過程中的信息不被別人截獲,但是細細思考下,HTTPS 是應用層協議,下層採用 SSL 保證信息安全,但是在客戶端和服務端,密文同樣是可以被截獲的;

2.HTTPS 報文在傳輸過程中,如果客戶端被惡意引導安裝 “中間人” 的 WEB 信任證書,那麼 HTTPS 中的 “中間人攻擊” 一樣會將明文密碼泄露給別人。

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

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

  1. 保證了用戶數據庫內部的密碼信息安全;

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

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

  4. 那太好了!這樣可以省下 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,是個非法請求。

  1. 太不容易了!可是還別高興的太早,當心數據被篡改

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

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

6.1 什麼是 “數字摘要”

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

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

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

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

因此,我們可以在 WEB 端對之前案例中提到的 username+MD5(password)+token 通過簽名,得到一個字段 checkCode,並將 checkCode 發送給服務器,服務器根據用戶發送的 checkCode 以及自身對原始數據簽名進行運算比對,從而確認數據是否中途被篡改,以保持數據的完整性。

  1. 總結

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

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

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

問題描述:

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

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

摘要或加密 JS 算法不直接以靜態文件的形式存在瀏覽器中,而是讓 WEB 端去請求 Server,服務器可以根據隨機令牌 token 值決定返回一個相應隨機的加密策略,以 JS 代碼響應的方式返回,在異步請求響應中,加載 JS 摘要算法,這樣客戶端就可以動態加載數字摘要策略,保證無法仿造。

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

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

問題描述:

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

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

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

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

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

CSDN 有一篇關於 MD5 風險的博客寫的非常好,推薦一下:MD5 算法如何被破解

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

原文:www.cnblogs.com/letcafe

作者 | letcafe;來源 | 博客園

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/R-N_fLJ1QighfgUnE8s3eg