因爲安全漏洞,社區網站被攻破或摧毀的案例已經不在少數。也因此,我們在立項做Discourse.org的時候,就把那些教訓銘記於心。儘管開源的社區軟件已經成千上萬,我們還在爲開發一個在骨子裏就非常安全的軟件而努力。
與此同時,我們也重視可移植性——你能隨意將數據從Discourse.org導入或導出。這也是Discourse遵從Creative Commons協議的原因——這一點有別於其他的論壇軟件。作爲一名普通用戶,你可以在用戶信息頁面,輕輕鬆鬆地將你在Discourse上發表的文字導出並下載下來。
注:Creative Commons(知識共享)是一個相對寬鬆的版權協議。使用者可以明確知道所有者的權利,不容易侵犯對方的版權,作品可以得到有效傳播。
如果你是站長,你可以在管理面板上輕易地備份或者還原整個網站的數據庫。在瀏覽器裏操作就行。我們一開始就爲你設置好了每週的自動備份。爲你考慮得這麼周到,算是你有福了!我可是親身經歷了一次慘痛的教訓之後,纔在備份方面成爲行家裏手的。
這麼多年下來,我們意識到,在安全和數據可移植性之間求得平衡是很難的。能把整個數據庫下載下來,最開心的莫過於黑客了,這意味着他們很快就能在你的系統中獲取據點。那可是終極大獎!
爲了緩解這種威脅,我們對Discourse的備份在很多方面逐步加強了限制:
- 管理員的密碼長度至少需要15個字符;
- 備份的創建和下載操作都有正式的日誌記錄;
- 備份的下載令牌是一次性的,會被email到管理員的郵箱,以確認用戶對郵箱地址有完全的控制權。
說到“安全”措施,無非就是深度防禦,因此所有這些加固手段都是有益的……但是,我們仍然需要假設,網上的一些壞傢伙能夠通過某種方式獲得你的數據庫的一份拷貝。然後怎麼辦呢?好吧,我們看看數據庫裏有些啥?
l 身份cookie
衆所周知,cookie是瀏覽器用以識別用戶身份的。cookie通常以哈希值的形式存儲,而不會是裸值。因此,暴露哈希值不至於讓你冒充目標用戶。況且,大部分現代的web框架都會快速回收cookie,因此它們的有效期只有短短的10~15分鐘。
l Email地址
儘管用戶對於自己的email被曝光這件事,多少還是有些擔心的。但現如今把email地址當成至寶的人應該也爲數不多吧。
l 所有的文章
我們假設這是一個完全公開的網站,沒人會在那裏發佈特別敏感的東西。既然所有的文章都是公開的,我們也就不必擔心什麼商業機密或其他祕密資料被揭露了(至少眼下不用擔心)。如果我們確實有一些機密資料要保護,那我以後可以專門寫一篇文章來探討這個問題。
l 密碼的哈希值
剩下的就是密碼的哈希值了。而且,那確實是個嚴重的問題!
現在的問題是,攻擊者拿到了你的數據庫,他們通過大規模的離線攻擊,或者在他們財力能夠承受的範圍內充分利用雲計算資源,攻破了密碼哈希。一旦他們得逞了,他們就能以那個用戶的身份登錄……一直這樣冒充着,直到那個用戶修改密碼。
★ 所以說:如果你知道(甚至只是懷疑)你的數據庫被暴露了,你應該做的第一件事就是重置所有人的密碼。
然而,如果你沒有意識到被拖庫了,怎麼辦呢?你需要先發制人,像世界最流氓大公司的IT部門那樣,每隔30天就重置所有人的密碼嗎?那樣的用戶體驗顯然很糟糕,還會把自身帶入一種嚴重的病態。現實是,當你的數據庫被暴露之後,你可能並不會知道,而等你知道的時候已經太晚了,那時採取任何措施都已枉然。因此,關鍵還在於拖慢攻擊者的節奏,爲自己爭取時間去處理和應對。
這樣的話,你可以提供給用戶唯一真正的保護,就只有你存儲的密碼哈希的抗攻擊性了。說到哈希的強度,有兩個因素:
1. 哈希算法。應該儘可能的慢,並且最好跑在GPU上時尤其慢。原因後文細述。
2. 工作因子或迭代次數。儘可能設置高一點,不過也不至於讓你陷入DoS攻擊爲限。
我看到過這樣的建議,說你應該把全局的工作因子設得足夠高,以至於在目標平臺上計算一個密碼的哈希值至少需要耗時8毫秒。事實證明,Sam Saffron(和我一樣是Discourse的聯合創始人)早在2013年選擇NIST(美國國家標準與技術研究院)推薦的PBKDF2-HMAC-SHA256和64k迭代,是多麼明智的一個決定啊!我們測試過,這個算法在我們目前的服務器(相當高端的Skylake 4.0 GHz)上,運行我們現有的Ruby登錄代碼,確實需要耗費大概8毫秒。
不過,那是4年前的事了。現如今,我們數據庫裏的密碼哈希還這麼安全嗎?4年以後或者10年以後呢?我們在做的是一個開源軟件,是一個長期項目,我們需要確信我們做出合理的決定,以保護每一個人。俗話說得好,防人之心不可無,是時候戴上達斯頭盔演一回壞人了——讓我們自己來攻擊自己的哈希!
我們先用惡貫滿盈的單GPU(GTX 1080 Ti)小試一下。先給些參考數據:PBKDF2-HMAC-SHA256在1080型號上可以達到1180 kH/s(千次哈希/秒),而在1080 Ti上可以達到1640kH/s。看吧,還是同一代視頻卡呢,在攻擊哈希的效率方面竟然能提升將近40%。細思極恐!
首先,我們來做一個小小的試驗,看看是否行得通。我下載了Hashcat。(注:Hashcat是當前最強大的開源密碼恢復工具,可以訪問Hashcat.net瞭解更多詳情。)我登錄進我們的演示網站try.discourse.org,然後創建了一個新帳號,並設置了密碼0234567890。接着,我查看了數據庫,發現爲那個新用戶生成了如下的哈希值和鹽:
hash 93LlpbKZKficWfV9jjQNOSp39MT0pDPtYx7/gBLl5jw=
salt ZWVhZWQ4YjZmODU4Mzc0M2E2ZDRlNjBkNjY3YzE2ODA=
Hashcat對輸入文件的格式是有要求的:一行只描述一個哈希,須註明哈希類型,迭代次數,鹽和哈希值,並且各部分之間用冒號隔開,如下:
sha256:64000:ZWVhZWQ4YjZmODU4Mzc0M2E2ZDRlNjBkNjY3YzE2ODA=:93LlpbKZKficWfV9jjQNOSp39MT0pDPtYx7/gBLl5jw=
讓我們把它交給Hashcat,看它是否搞得定!
./h64 -a 3 -m 10900.\one-hash.txt 0234567?d?d?d
注意:這裏故意降低了計算量,只讓它猜3個數字。而不出所料的是,我們很快就把密碼攻破了!看到最後的密碼了嗎?我們得手了!
sha256:64000:ZWVhZWQ4YjZmODU4Mzc0M2E2ZDRlNjBkNjY3YzE2ODA=:93LlpbKZKficWfV9jjQNOSp39MT0pDPtYx7/gBLl5jw=:0234567890
到目前爲止,我們知道上面的攻擊方法是可行的。然後讓我們進入正題。不過,我們一開始先簡單一點——蠻力攻擊最簡單的8位數字組成的Discourse密碼需要多久呢——也就108個組合,1個億吧。
Hash.Type........:PBKDF2-HMAC-SHA256
Time.Estimated...:Fri Jun 02 00:15:37 2017 (1 hour, 0 mins)
Guess.Mask.......:?d?d?d?d?d?d?d?d [8]
即使用的是一款極品GPU……記住,這裏我們只是測了一個哈希,也就是說,表中的每一行(用戶)都需要耗費你1小時。還有更打擊你的消息:Discourse已經禁用8個字符的密碼有段時間了。如果我們嘗試破解更長的數字型密碼,需要花多少時間呢?
?d?d?d?d?d?d?d?d?d[9]
Fri Jun 02 10:34:422017 (11 hours, 18 mins)
?d?d?d?d?d?d?d?d?d?d[10]
Tue Jun 06 17:25:192017 (4 days, 18 hours)
?d?d?d?d?d?d?d?d?d?d?d[11]
Mon Jul 17 23:26:062017 (46 days, 0 hours)
?d?d?d?d?d?d?d?d?d?d?d?d[12]
Tue Jul 31 23:58:302018 (1 year, 60 days)
要知道,全數字的密碼模式太簡單了,小朋友纔會用!要不要試一些真實的密碼,比如只含小寫字母,或者小寫+大寫+數字的組合?
Guess.Mask.......:?l?l?l?l?l?l?l?l [8]
Time.Estimated...:Mon Sep 04 10:06:00 2017 (94 days, 10 hours)
Guess.Mask.......:?1?1?1?1?1?1?1?1 [8] (-1 = ?l?u?d)
Time.Estimated...:Sun Aug 02 09:29:48 2020 (3 years, 61 days)
目前看來,像這樣的蠻力攻擊——拿字母或數字一個一個去試——在高端GPU上的表現也不算驚豔。但如果我們把數字除以8呢……手段是,在一臺機器裏裝上8張顯卡。這是小公司財力所能及的,或者有些有錢的個人也能做到。遺憾的是,38個月除以8得出的結果也並沒有大幅降低攻擊時間。別急,如果以舉國之力來做這件事呢?他們財力雄厚,可以使用幾千個這樣的GPU(1.1天),甚至可能幾萬個GPU(2.7小時),然後呢……好了,即使密碼的最低要求是10個字符,你在那種情況下也麻煩大了!
如果我們想讓Discourse扛得住全國性的攻擊,很顯然,我們還需要努力。Hashcat有一個方便的基準模式,它在一臺裝有8張Nvidia GTX 1080 GPU的設備上測試,然後根據結果按順序列出了最強(最慢)的哈希算法。從那份清單上,我看到了脫穎而出的bcrypt、scrypt和PBKDF2-HMAC-SHA512。
我用Hashcat的測試結果給了我一些信心,說明Discourse存入數據庫的密碼哈希並沒有太偏離正確軌道。但是,我想要的是完全的確信,於是我請了一位有安全和穿透測試背景的人,來嘗試破解當前託管在我們這兒的兩個非常熱門的Discourse站點的密碼哈希(當然簽了保密協議)。結果是這樣的:
有人提供給我兩套來自兩個不同的Discourse社區的密碼哈希,分別包含5909個和6088個哈希值。兩者都使用了PBKDF2-HMAC-SHA256算法,並且工作因子是64k。我的機器配有Nvidia GTX1080 Ti GPU,使用Hashcat計算哈希的速率大約是每秒27000次。
所有Discourse社區都共同遵循的密碼規則如下:
- 所有用戶必須擁有一個最小長度是10個字符的密碼;
- 所有管理員的密碼長度至少是15個字符;
- 用戶不能使用10000個最常用的密碼黑名單上的密碼;
- 用戶可以選擇創建新的用戶名和密碼,或者使用各種第三方認證機制(Google、Facebook、Twitter等)。如果用戶選擇了這個選項,就會自動生成一個安全的32個字符長度的隨機密碼。給定的任何密碼,是無法區分它是人工輸入的還是自動生成的。
利用上述密碼規則制定出模板,我花了大概3周時間在11997個哈希裏破解出了39個,其中25個是xxx社區的,14個是yyy社區的。
這是一位安全領域的研究員,他經常做如此這般的審計。因此,所有的攻擊都使用了字典,並且根據他以前的經驗總結出來的高效攻擊模式——這不是真正意義上的蠻力攻擊。他恢復出來的密碼如下(其中有一個是重複的):
007007bond | l3tm3innow |
如果我們把攻擊力度提升到8倍,並且允許的時間翻倍,假設碰到一個打了雞血的攻擊者,或者他有一套很靈光的字典和模板,可以預見,他可能最後破解出 39 x 16 = 624個密碼,也就是相當於所有用戶中的5%。這是合理的估算,但仍然比我想象的要高。在Discourse的未來版本里,我們肯定會規劃加入哈希類型表。這樣我們就能在將來1~2年裏,切換到一種更爲安全(慢得多)的密碼哈希策略。
bcrypt$2*$, Blowfish (Unix)
20273 H/s
scrypt
886.5 kH/s
PBKDF2-HMAC-SHA512
542.6 kH/s
PBKDF2-HMAC-SHA256
1646.7 kH/s
通過這次演練,我現在對我們最糟糕的安全場景有了一個更爲深入的理解,也就是,數據庫被盜走,然後在線下遭遇專業的密碼哈希攻擊。同時也讓我更加自信地推薦和支持我們的開發工作,使得Discourse的用戶人人都很安全。因此,如果你在安全實施方面並不完全確信(像我一樣),是時候來驗證一下那些假設啦。別等着黑客來攻擊你——作爲黑客,要勇於自黑!