(原文發佈於 Cymetrics Tech Blog:為什麼忘記密碼時只能重設,不把舊密碼告訴我?)
某天小明在整理他的我的最愛(到底誰的),發現了一個以前很常逛,但已經將近半年多沒去的一個論壇。小明想回去看看那邊變得怎麼樣了,於是點進去那個論壇,輸入了帳號密碼,得到了密碼錯誤的回覆。
嘗試了幾次之後,系統提示小明可以使用「忘記密碼」的功能,所以小明填了自己的 email 之後去信箱收信,發現系統傳來一個「重設密碼」的連結。雖然說最後小明成功利用重新設定的密碼登入,但有個問題讓他百思不得其解:
「奇怪欸,幹嘛要我重設密碼,為什麼不把舊的密碼寄給我就好?」
應該有許多人都跟小明一樣,有過類似的疑惑。把舊密碼寄給我不是很好嗎,幹嘛強迫我換密碼?
這一個看似簡單的問題,背後其實藏了許多資訊安全相關的概念,就讓我們慢慢尋找問題的答案,順便學習一些基本的資安知識吧!
先提醒一下,雖然說前半段看起來可能跟文章主題無關,但好酒沉甕底,我保證最後會把這些東西關聯起來。
被偷走的資料庫
大家應該很常看到新聞說哪個網站的資料又被偷走了,顧客個資全部都外洩出去。像是國外知名的網域代管網站 GoDaddy 就外洩了 120 萬筆用戶資料,而台灣之前也有過愛心協會捐款個資外洩的狀況發生。
這邊我想帶大家探討的兩個問題是:
- 資料真的這麼容易外洩嗎?
- 資料外洩之後,可能造成什麼後果?
我們先來看第一個問題,有很多安全性的漏洞可以造成資料外洩,而有些漏洞的攻擊方式,比你想的還簡單一百倍。
你想像中的駭客可能像上面那樣,打著一大堆不知道在幹嘛的指令,畫面上出現很多黑底白字或是綠字的畫面,完全搞不懂在幹嘛,但是做著做著網站就被打下來了。
而事實上有些漏洞,可能在網址列上面改幾個字就攻擊成功了,就算你不懂任何程式也做得到。
舉例來說好了,假設今天有個購物網站,你買了一些東西之後送出訂單,訂單成立後跳轉到訂單頁面,上面有著一大堆你的個資,例如說:姓名、收貨地址、聯絡電話以及 Email 等等。
然後你發現訂單頁面的網址是 https://shop.huli.tw/orders?id=14597
而正好你的訂單編號也是 14597,在好奇心的驅使之下,你就試著把數字改成 14596,然後按下 Enter。
當網站載入完成之後,你竟然還真的能看到編號為 14596 的訂單,上面出現一個你不認識的人的姓名、收貨地址、聯絡電話跟 Email。
有些攻擊就是這麼樸實無華且枯燥,只要改個字就能看到屬於其他人的資料。這時候如果你會寫程式的話,就可以寫個腳本自動去抓 id 是 1 一直到 id 是 15000 的資料,你就拿到了這個購物網站 15000 筆訂單的資訊,也就是一萬多個顧客的個資。
這過程中沒有什麼黑底白字的畫面,也不用一直瘋狂打字,唯一需要的只有改數字,個資就輕鬆到手。
這類型的漏洞有個專有名詞,稱為 IDOR,全名是:Insecure direct object references,大約就是不安全的直接資料存取的意思。漏洞產生的原因就是工程師在開發時,並沒有注意到權限控管,因此讓使用者能存取到其他人的資料。
有些人看到這邊可能以為我只是為了文章淺顯易懂,所以才舉一個簡化的例子,現實生活中的攻擊才沒這麼簡單。
這句話算是對了一半,大部分的網站確實都不會有這麼明顯的一個漏洞,攻擊方式會更複雜一點。但可怕的是,還真的有些網站就是這麼簡單,就是改個數字就可以拿到別人的資料。
台灣有一個網站叫做 HITCON ZeroDay,是由台灣駭客協會所維護的漏洞回報平台。有些人發現漏洞之後可能會竊取個資拿去賣,從事非法行為,也有些人發現漏洞只是為了鍛鍊技術,並沒有想要做什麼壞事。
因此就可以透過這個平台進行回報,回報漏洞之後負責維護平台的志工們會幫你驗證漏洞,驗證過後回報給負責的廠商,讓他們去修復漏洞。
這個平台上的漏洞在修復過後隔一陣子會公開,或者儘管廠商沒有回報修復,過一陣子(例如說兩個月)後也會公開,因此在這平台上可以找到許多公開的漏洞,看過之後你大概就不會想在網站註冊時留下真實個資了…
例如說這兩個就是 IDOR 的真實漏洞:
對,不要懷疑,就真的只是在網址上改個數字而已這麼容易。
以後只要看到網址列上有這種數字,就可以試著去改改看,搞不好不會寫程式的你也可以發現 IDOR 的漏洞。
除了這種只要改個東西的漏洞之外,還有另外一個很常見但是需要一點技術能力才能攻破的漏洞,叫做 SQL Injection。
先來講講 SQL 是什麼,簡單來說就是跟資料庫查詢東西的一種程式語言。既然說是語言那就會有固定語法,若是以中文舉例,大概就像是:
去找「訂單資料」,給我「id 是 1 的」,按照「建立時間」排序
用「」框起來的部分代表可以變動,而其他關鍵字例如說「去找」、「給我」這些都是固定的,因為語法要固定才能寫程式去解析。
同樣以上面假想的購物網站為例,如果網址是 https://shop.huli.tw/orders?id=14597,那網站去跟資料庫拿資料時,指令大概就是:
去找「訂單資料」,給我「id 是 14597 的」
因為網址列上的 id 是 14597 嘛,所以這個 id 就會被放到查詢的指令去,如果 id 是別的,那查詢的指令也會不一樣。
這時候如果我的 id 不是數字,而是「1 的順便給我使用者資料」,查詢就變成:
去找「訂單資料」,給我「id 是 1 的順便給我使用者資料」
那整個網站的使用者資料就順便被我抓下來了。
這個攻擊之所以叫做 SQL injection,重點就在於那個 injection,攻擊者「注入」了一段文字被當作指令的一部分執行,所以攻擊者就可以執行任意查詢。
比起上面講的 IDOR,SQL injection 通常會更為致命,因為不只是訂單資料本身,連其他資料也會被一起撈出來。所以除了訂單資料,會員資料跟商品資料都有可能一起外洩。
這邊也隨便找兩個公開的案例:
而防禦方式就是不要把使用者輸入的「1 的順便給我使用者資料」直接當作指令,而是經過一些處理,讓整段查詢變成:「給我 id 是:『1 的順便給我使用者資料』的資料」,那因為沒有這個 id,所以什麼事也不會發生。
個資洩漏了,然後呢?
前面我們已經看到了針對那些沒有做好防禦的網站,個資外洩是多麽容易的一件事情。
那個資洩漏之後,對使用者會有什麼影響呢?
大家最感同身受的應該就是詐騙電話吧,例如說某些買書的網站或是訂房網站,打過來跟你說什麼要分期退款,為了博取你的信任,連你買了哪本書,訂了哪個房間,或是你家地址跟姓名全都講得出來。
這些都是因為資料外洩的緣故,詐騙集團才會知道的這麼清楚。
但除了這些個資以外,還有兩個東西也會外洩,那就是你的帳號跟密碼。
也許你會想說:「不就帳號跟密碼嗎,我就在那個網站上面改密碼以後再用就好啦!」
事情也許沒有你想的這麼簡單。如果你沒有用密碼管理軟體的話,我大膽猜測你所有的密碼可能都是同一組。因為怕記不起來嘛,所以乾脆都用同一組密碼。
這時候如果帳密外洩,駭客是不是就可以拿這組帳密去其他服務試試看?
拿去登你的 Google,登你的 Facebook,這時候用同一組密碼的人就會被登進去。所以從表面看只是一個購物網站被入侵,但造成的結果卻是你的 Google 還有 Facebook 也一起被盜了。
所以,有時候某個網站被盜帳號可能不是那個網站的問題,而是駭客在其他地方拿到了你的帳號密碼,就來這邊試試看,沒想到就中了。
對於網站的開發者而言,保護好使用者的個資是天經地義的事情,保護密碼也是,有沒有什麼好方法可以保護密碼呢?
加密嗎?把密碼用某些演算法加密,這樣資料庫儲存的就會是加密後的結果,儘管被偷走了,駭客只要沒有解密的方法就解不開。
聽起來似乎是最安全的做法了,但其實還有一個問題,那就是網站的開發者還是會知道怎麼解密,如果有工程師監守自盜怎麼辦?他還是可以知道每個使用者的密碼是什麼,可以把這些資訊拿去賣或者是自己利用。
嗯…似乎我們也不能怎麼樣,因為無論如何,開發者都需要有方法知道資料庫存的密碼究竟是多少吧?不然在登入的時候怎麼確認帳號密碼是對的?
再者,這樣聽起來應該夠安全了,要怎麼樣才能更安全?難道要連網站的開發者都無法解密,都不知道密碼是什麼才夠安全嗎?
Bingo!答對了,就是要這樣沒錯!
沒有人知道你的密碼,包括網站本身
事實上,網站的資料庫是不會儲存你的密碼的。
或更精確地說,不會儲存你的「原始密碼」,但會儲存密碼經過某種運算後的結果,而且最重要的是,這個運算是無法還原的。
直接舉例比較快,假設今天有個很簡單的演算法,可以把密碼做轉換,轉換方式是:「數字不做轉換,英文字母把 a 換成 1,b 換成 2…z 換成 26」,以此類推,第幾個字母就換成幾,大小寫不分都一樣(先假設不會有符號)。
如果密碼是 abc123,轉換完就變成 123123。
在使用者註冊的時候,網站就把使用者輸入的 abc123 轉成 123123,然後存到資料庫裡面。因此資料庫存的密碼是 123123,而不是 abc123。
當使用者登入時,我們就再把輸入的值用同樣的邏輯轉換,如果輸入一樣,轉換後的結果就會一樣對吧?就知道密碼是不是正確的。
當駭客把資料庫偷走以後,會拿到 123123 這組密碼,那一樣啊,不是可以推論出原本是 abc123 嗎?不不不,沒這麼簡單。
123123、abcabc、12cab3…這些密碼轉換之後,不也是 123123 嗎?所以儘管知道轉換規則跟結果,卻沒有辦法還原成「唯一一組密碼」,這就是這個演算法厲害的地方!
這樣的轉換就叫做雜湊(Hash),abc123 每次 hash 過後的結果都會是 123123,但是從 123123 卻無法推回輸入一定是 abc123,因為有其他種可能性存在。
這就是 hash 跟加密最大的不同。
加密跟解密是成對的,如果可以加密就一定可以解密,所以你知道密文跟密鑰,就可以知道明文。但 hash 不同,你知道 hash 的演算法跟結果,卻無法回推出原本的輸入是什麼。
而這個機制最常見的應用之一,就在於密碼的儲存。
在註冊時把 hash 過後的密碼存進資料庫,登入時把輸入的密碼 hash 過後跟資料庫比對,就知道密碼是否正確。就算資料庫被偷,駭客也不知道使用者的密碼是什麼,因為回推不出來。
這就是為什麼忘記密碼的時候,網站不會跟你講原本的密碼是什麼,因為網站本身也不知道啊!
所以不能「找回密碼」,只能「重設密碼」,因為重設就代表你輸入新的密碼,然後網站把新的密碼 hash 之後存進資料庫,未來登入時就會用這組新的 hash 去比對。
有些人可能會注意到這樣的儲存方式似乎有個漏洞,延續前面的例子,資料庫存的是 123123 而我的原始密碼是 abc123,這樣如果用「abcabc」,hash 過後也是 123123,不就也可以登入嗎?這樣不太對吧,這不是我的密碼欸
有兩個不同的輸入卻產生出同一組輸出,這種狀況稱為碰撞(hash collision),碰撞一定會發生,但如果演算法設計的好,碰撞的機率就超級無敵小,小到幾乎可以忽略。
前面提的轉換規則只是為了方便舉例,真實世界中用的演算法複雜許多,就算只有一個字不同,結果都會天差地遠,以 SHA256 這個演算法為例:
- abc123 => 6ca13d52ca70c883e0f0bb101e425a89e8624de51db2d2392593af6a84118090
- abc124 => cd7011e7a6b27d44ce22a71a4cdfc2c47d5c67e335319ed7f6ae72cc03d7d63f
類似的輸入卻產生截然不同的輸出。
像我前面舉例用的轉換就是不安全的 hash 演算法,要盡量避免使用或是避免自己設計,盡可能使用密碼學家跟專家設計出的演算法,像是上面提到的 SHA256。
在使用這些演算法的時候,也要特別注意一下是否安全,因為有些演算法雖然也是由專家設計,但已經被證明是不安全的,例如說密碼用 MD5 來 hash 後儲存就是不安全的,可以參考:Is MD5 considered insecure?
所以,儲存 hash 後的值就沒事了嗎?
抱歉,其實只儲存密碼 hash 過後的值是不夠的。
咦,為什麼?我剛剛不是說沒辦法反推出結果嗎,那為什麼不夠?
雖然說沒辦法反推出結果,但攻擊者可以利用「輸入一樣,輸出一定一樣」的特性,先建好一個資料庫。
舉例來說,假設有個很常見的密碼 abc123,hash 過後的值是 6ca13d,那攻擊者就可以先算好,然後把這個關係存在資料庫,所以攻擊者的資料庫裡面就可能會有一百萬組最常見密碼的清單,裡面有著每個密碼跟它 hash 過後的值。
那接下來只要在 hash 過後的資料庫發現 6ca13d,攻擊者就可以透過查表的方式,查出原本的密碼是 abc123。這不是利用演算法反推結果,這只是利用現有資料來查詢而已。
為了防禦這種攻擊,還要做一件事情叫做加鹽(Salting),沒錯,就是鹽巴的那個鹽。通常會幫每個使用者產生一個獨一無二的鹽巴,例如說 5ab3od(實際上會更長,可能 16 或 32 個字以上),接著把我的密碼 abc123 加上我的鹽巴,變成 abc1235ab3od,然後用這個加鹽過後的結果去做 hash。
為什麼要這樣做呢?
因為攻擊者預先準備好的表格中,比起 abc123,出現 abc1235ab3od 的機率顯然更低,同時又因為長度變長了,暴力破解的難度變得更高。如此一來,密碼就變得更難破解了。
更多資訊請參考:不是祕密的祕密
結語
忘記密碼時網站不會把密碼寄給我,因為網站自己都不知道我的密碼是什麼。雖然聽起來不太可能,但實際狀況就是如此。為了安全性,這是必須的手段。
要達成這樣的目的,背後最重要的技術原理就是 hash,「同樣的密碼會產生同樣的 hash 值,但從 hash 值沒辦法對應回原本的密碼」就是秘訣所在。
反之,如果你發現有網站可以找回你的密碼,那就得要多加注意,有可能網站資料庫存的不是 hash 值而是你的密碼。在這種狀況下,萬一有天資料庫被入侵,帳密被偷走,駭客就能得知你真實的密碼,然後去試其他的服務。
有關於密碼管理,現在瀏覽器也有功能可以自動幫你產生密碼外加記憶密碼,或也可以使用現成的密碼管理軟體,都可以在不同網站產生不同的密碼。
這篇希望能讓對這個領域陌生的讀者們也能知道一些基本的概念,包括:
- 有些網站比你想得脆弱很多,改個網址就可以拿到別人的資料
- 對於安全性做得不好的網站,拿到整個資料庫不是一件難事
- 忘記密碼只能重設,不能找回,是因為網站也不知道你的密碼
- 如果有網站可以把舊密碼給你,那你得要小心一點