以 Heartbleed 為起點
對於最近 OpenSSL 漏洞所引發的強烈反應,是可以理解的:當整個網際網路突然都需要修補時,絕對不是什麼愉快的事。再者,就我個人而言,這個漏洞竟如此瑣碎、如此單純,反而令人不安。我無意指責 OpenSSL 的開發者,但你通常會以為,像 OpenSSL 這樣的軟體,就算出問題,也應該是更細微、更隱晦的那一類。通常是沒有把合理性檢查*正確地*做好,而非像這個漏洞這樣,在 memcpy() 呼叫中完全*缺乏*邊界檢查。 不過,有時候一早起來重看自己前一晚寫的程式碼,也會感到非常難為情。程式設計師本來就會犯錯,我自己就經常犯錯,所以我認為,需要改變的是流程,而不是換掉整個 OpenSSL 團隊。 有人主張應該改用比 C 更安全的語言,也有人認為是規格本身出了問題,因為它太過複雜。這兩種說法或許都有些道理,然而短期內我們不太可能轉換到不同的規格或系統語言,因此真正的問題是,我們現在能做些什麼來提升系統軟體的安全性? 1) 砸錢解決。 只要有投入,要讓系統程式碼更安全其實很單純。如果有不同的公司聘請資安專家針對 OpenSSL 的程式碼庫進行程式碼稽核(code auditing),那麼像 Heartbleed(心臟出血) 這類漏洞被發現的機率就會大幅提高。 我曾見過一些非常複雜、需要多個非顯而易見的條件同時成立才會觸發的漏洞,透過嚴謹的程式碼稽核而被發現。而一個沒有邊界檢查的 memcpy(),如果是以資安角度來檢視程式碼,在第一次閱讀時就會格外顯眼。猜猜 Heartbleed 是怎麼被發現的?正是透過在 Google 進行的安全稽核。 把開放原始碼當成只取不予的資源的時代,或許已經結束了。許多公司應該效法 Google 與其他企業的做法,投入人力參與 OSS 軟體的開發與安全工作。 2) 靜態與動態檢查。 Static code analysis(靜態程式碼分析)就其附帶效果而言,是一種半自動化的程式碼稽核方式。在像 OpenSSL 這樣關鍵的系統程式碼中,即使為了讓靜態分析更有效而進行一些原始碼標註或採用一套規則,也是完全可以接受的。 今日的靜態分析工具並非萬靈丹,但若由經驗豐富的程式設計師仔細檢視靜態分析的輸出結果,仍能帶來一定的價值。 另一大助力則來自像 Valgrind 這類的動態檢查工具。任何以 C 語言撰寫的系統軟體,都應該在每次提交新程式碼時,自動透過 Valgrind 進行測試。 3) 以函式庫來抽象化 C。 C 是低階語言,語言本身沒有內建的安全機制。然而 C 的一個優點是,它允許你在其原始、底層的基礎之上建構更高層次的抽象。 一個設計健全的動態字串函式庫能避免大量的緩衝區溢位問題,而如今幾乎每個像樣的專案都在使用它。然而你還能做得更多。舉例來說,對於可能包含私鑰等敏感資料、對安全性要求極高的程式碼,你可以在動態字串函式庫中擴充記憶體複製的原語(primitive),讓它在從一個緩衝區複製到另一個緩衝區時,自動執行隱含的合理性檢查。 此外,如果某個緩衝區包含關鍵資料,你可以設定邏輯權限,使得任何試圖從該區域複製資料的行為都會導致程式中止。還有其他較缺乏可攜性、透過記憶體管理來更有效地保護重要記憶體分頁的方法,不過在現實世界中,基於可攜性與可預測性的考量,較高層次的 C 語言層級保護往往反而更簡單實用。 整體而言,有許多方法可以探索,以避免在毫無保護的情況下直接使用 C,透過建立一個建構於其上的抽象函式庫,讓程式設計變得更安全。 4) 隨機化測試。 單元測試不太可能觸發邊界案例或未通過的合理性檢查。有一類已經存在數十年的測試方法,在我看來仍未被充分利用:fuzzy testing(模糊測試)。 OpenSSL 的這個漏洞,絕對可以透過發送不同種類、帶有不同隨機化參數的 OpenSSL 封包,並結合像 Valgrind 這類動態分析工具來發現。 以我的經驗,大量隨機化測試搭配一個讓程式在 Valgrind 上反覆執行相同測試的環境,能夠發現許多在其他情況下會被忽略的真實世界漏洞。有許多模型可以探索,通常你會想要一種能注入完全隨機資料的模型,以及一些將合法封包以不同隨機方式加以破壞的中間模型。 這類技術的一個典型例子是早年的 DNS 壓縮無窮迴圈漏洞。只要向一個簡易的實作丟幾個隨機封包,幾分鐘內就能找到問題。 5) 轉變對安全性與效能的思維。 有趣的是,OpenSSL 之所以自行實作配置快取(allocation caching)機制,是因為在某些系統上 malloc/free 的速度很慢。這顯示即便在攸關安全的關鍵程式碼中,效能仍被賦予了過高的重視,甚至超過了安全性。就這個具體案例而言,必須承認,OpenSSL 開發者在封裝 malloc 時,很可能從未想過這麼做會帶來的安全隱憂。然而,他們會去關心像*某些*系統上配置函式這類低階細節,本身就顯示出對效能的深度關切,而他們其實更應該深刻關切系統的正確性與安全性。 整體來說,無助於改善現況的是,作為當今伺服器基礎架構事實上標準的系統——也就是 Linux——長期以來擁有、且至今仍擁有你所能找到最糟的記憶體配置器之一,這在很大程度上是出於授權考量,因為較好的配置器並非採用 GPL 授權,而是 BSD 授權。 這恐怕又是大型企業應該做出貢獻的另一個領域,透過對 glibc malloc 提供重大的改進。即使已有更好的替代方案,glibc malloc 仍然是許多現實世界中的系統軟體實際上會採用的配置器。 我很希望看到關於 Heartbleed 的討論能採取更務實的態度,因為有一件事是可以確定的:無論是責怪這方或那方,都不會改變 OpenSSL 或其他任何軟體實際的安全水準,而未來還有新的挑戰。例如,HTTP/2.0 的實作在安全性方面就可能是一個非常敏感、關鍵的時刻。 編按:其實是我弄錯了,Glibc 內部的 malloc 實作是採用 BSD 授權,所以並非授權問題。我不知道為什麼 Glibc 不改用 Jemalloc,那是一個非常優秀且持續積極開發中的配置器。
隨機一篇部落格