PageRank



轉移公告

計劃把 http://blog.hoamon.info/ 文章全部轉移至 http://www.hoamon.info/blog/ 這裡,而本 Blogger 站台的文章近 500 篇,我預計在 2014-12-31 前移轉完畢,完成後 http://blog.hoamon.info/ 將只作代轉服務,一律把舊連結如 http://blog.hoamon.info/index.html 轉成 http://www.hoamon.info/blog/index.html ,敬請舊雨新知互相走告。

新文章只發佈在 http://www.hoamon.info/blog/

何岳峰 敬上

2012年1月9日 星期一

不應該在資料庫中紀錄使用者的明碼密碼

看到這個有趣的 Blog :「我的密碼沒加密」。我才知道這世上不懂『不應該在資料庫中紀錄使用者的明碼密碼』的程式設計師是如此之多。

這本應是菜鳥程式設計師才應該犯下的錯,沒想到連存在好幾年的「綠界」都有。我真的不願相信這是事實。話說回來,對一個合格的 Linux/Unix 管理員,不能有明碼密碼根本就是常識。

過去,我還想用 PGP 驗證來取代『在伺服器資料庫紀錄使用者密碼』,這法子絕對能確保「使用者密碼」不會外洩。只是,我沒遇到那個使用者能接受自己產生一對公私錀,利用它們來作登入的。

雖然我沒作出個 PGP 驗證的實際上線系統,但 StartSSL.com 作到了,它不是用 PGP 加解密,是另一種公私錀原理。所以外國月亮比較圓是真的。

在我所撰寫過的系統中,只要有帳號認證的,一定會在伺服器端用 md5/sha1 作 hash ,另外也會在瀏覽器端用 javascript 作 md5 hash 。

伺服器端 hash 是防自己人及入侵者,防入侵者不用講了,這是一定要的,但「自己人」為什麼要防呢? 想想看,一個系統內幾千甚至上萬個會員的帳號/密碼都可以給自己人看,那這些自己人難道不會換工作,難道不會對老闆不爽,難道不會被「社交」,將密碼變暗碼,除了保護使用者外,也是為自己人免責,至少使用者自己被社交了,他也不能栽髒我們。

而瀏覽器端的 md5 hash 又是為了什麼? 因為我寫的網頁系統少能跑在 https 上,無法為使用者輸入的密碼作加密以防止中間人攻擊。所以我只好用 md5 函式 hash 過使用者明碼密碼,這雖無法完全避免使用者帳戶被盜用(在明碼傳輸中,它就是有可能遭受中間人攻擊),但至少讓它們難以反推用戶的原始密碼。

另外也曾在一個「歷史系統」中,在瀏覽器端部份是使用 rsa 加密的。因為它之前只有使用「伺服器端 hash」作保護,而當我要再加上「瀏覽器端 hash 」功能時,會面臨「無法使用它的明碼密碼來作自我 hash 」。

是的, hash 也是有技巧的,不要用同一個 salt key 作,要不然入侵者破了一個使用者密碼後,其他的也都猜出來了。所以瀏覽器端,我都是用使用者原始密碼內的某個字去當 salt 來作 hash 的。

於是在「歷史系統」中,要把它一次轉成「有瀏覽器端 hash 」功能的話,勢必要每個使用者回來登入一次,我才有機會將系統轉移。這不是件容易的事。退而求其次,使用 rsa 加密就是不錯的方法。這方法是某個學弟找到的,原本我還不知道有原生的 js 函式庫能作 rsa 加密呢!

提了這些方法說明保護使用者明碼密碼不是作不到,只是有點麻煩而已。

然而那些程式設計師真的只是怕麻煩才不想使用 hash 密碼嗎? 根本不懂的程式設計師,就不要講了,剩下沒用 hash 的,除了怕麻煩外,我猜還怕另一樣東西: debug 。

紀錄「使用者明碼」密碼的惟一好處就是能操作「使用者」所能看到的頁面。

試想一個場景,某天使用者打客服電話,詢問歷史訂單事宜,他一直抱怨看不到之前的一筆 Ubuntu NB 訂單,可是客服用後台管理系統查詢,卻明確地說明使用者真的有這筆訂單。那這問題要如何解決?

如果客服有使用者的帳密,他是不是自己登入系統,來到相同頁面觀看,就能了解到底是系統有 bug ,還是使用者不會操作。有可能問題只是出在「訂單分頁」不夠明確,使用者不知道要點到第二頁去觀看訂單。

讓客服知道明碼密碼以便可以看到與使用者相同介面的網頁是不得以的方法嗎? 不。我們有更安全的作法。

我們的作法是在客服後台,有一個轉移帳戶的頁面,讓他填入帳號(且不用輸入密碼)後可轉換成任一個使用者,我們再紀錄那一個客服曾轉換成誰,又作了什麼,這樣客服不用知道使用者的明碼密碼,也可以變成那一個使用者,而且我們也知道客服幹了什麼好事,所以客服可以在面臨無法排除的困難時才會丟 ticket 給我們,這不是三方皆喜:使用者安全、客服好作事、程式設計師省事。

那這架構怎麼作? 因為程式語言、框架百百種。我只說明觀念,請程式設計師們自行發揮,當然有問題歡迎發問,或覺得我的方法可以改進的,也歡迎指教。

認證系統多半是使用 Session 架構來確認連線的使用者身份為何? 其他用 cookie 或是 GET 連結的也差不多,都是把使用者識別 ID (可能是使用者帳號,也可能是臨時編號)藏在某個變數之中,讓程式在執行時,能找到一個 key 值去對應資料庫中的使用者。

所以我們只要在那個對應資料庫使用者的程式中,多加上一個判斷式,如果有另一個轉換識別 ID 時,就把原使用者識別 ID 換成這個轉換識別 ID 。

例如: 何阿蒙客服登入系統後,連線 request 的使用者識別 ID 為 hoamon ,當他設定好轉換帳號為 grace 後,連線的 request 物件多了一個 grace 的轉換識別 ID 。這樣「對應資料庫使用者的程式」在運行時,就會知道使用者得用 grace 去代換,而不是 hoamon 。而我們的 log 函式在發現 request 物件多了轉換識別 ID 後,也會把『 hoamon => grace 』及所作的事一起紀錄到資料庫中。

這樣就能完全排除「明碼密碼」的使用了。

4 則留言:

  1. 其實 MD5/SHA1 也不怎麼安全... 不過有總比沒有好

    回覆刪除
  2. 我的主要目的不是要打造一個無法被盜用身份的網站,我只有要有心人不能反推「原始明碼」而已,而 md5/sha1 一定作得到,因為它們都是「多對一」函式。像是:

    '$A1' => 'b7879344f32d46655b1452468d224642',
    而 'hoamonIsPythoner' => 'b7879344f32d46655b1452468d224642',
    ... => 'b7879344f32d46655b1452468d224642'

    有非常多的字串在作完 md5 hash 後,都是 'b7879344f32d46655b1452468d224642' ,那麼對有心人來說,他如何知道那一個才是原始明碼呢?

    回覆刪除
  3. 嗯,不過因為 MD5 已經有 rainbow table 可以直接查表破解了,所以還是很危險 XD

    回覆刪除
  4. 感謝賜教。有了彩虹表,的確可以把「不太可能」的事變成「有可能而且還可以很快查出」。

    或許還是用公私錀才是一個萬全的驗證方法。

    回覆刪除

Related Posts Plugin for WordPress, Blogger...