Using Heartbleed as a starting point

Salvatore Sanfilippo

以 Heartbleed 為起點

原文由 Salvatore Sanfilippo 發布,訂閱此部落格

最近 OpenSSL 漏洞引發的強烈反應是可以理解的:當整個網際網路突然都需要修補時,實在不是什麼愉快的事。再者,就我個人而言,這個漏洞竟如此低級,更讓人不安。我無意指責 OpenSSL 的開發者,但通常會以為,像 OpenSSL 這樣的軟體,就算有這類問題,也應該要更隱晦一點才對。通常是沒能*正確地*做好 sanity check,而不是像這個漏洞這樣,在 memcpy() 呼叫中*完全沒有*邊界檢查。

不過,有時候早上起來重看自己前一晚寫的程式碼,我也會感到非常難為情。程式設計師本來就會犯錯,我自己就常常犯錯,所以我猜,我們需要的不是換掉 OpenSSL 團隊,而是換一套流程。

有人提議改用比 C 更安全的語言,也有人主張規格本身就有問題,因為太過複雜。這兩種說法或許都有些道理,不過短期內要換掉規格或系統語言並不現實,所以真正的問題是,現在我們能做什麼來提升系統軟體的安全性?

1) 砸錢下去。

如果有投入,要讓系統程式碼更安全其實很單純。如果不同的公司都聘請安全專家來對 OpenSSL 的程式碼庫進行審計,那麼發現像 Heartbleed 這類漏洞的機率就會大得多。

我曾看過一些非常複雜、需要多個非顯而易見的條件才會觸發的漏洞,就是靠認真的程式碼審計才被找出來。像 memcpy() 沒有做邊界檢查這種問題,只要你是從安全的角度去審視程式碼,第一次閱讀就會發現。猜猜 Heartbleed 是怎麼被發現的?就是透過 Google 進行的安全審計。

或許,把開源當成只拿不給的時代已經過去了。許多公司應該效法 Google 等企業,投入人力來參與 OSS 的開發與安全工作。

2) 靜態與動態檢查。

靜態程式碼分析附帶的好處,就是一種半自動化的程式碼審計方式。對於像 OpenSSL 這樣關鍵的系統程式碼,就算要做一些原始碼標註,或是套用一套規則來讓靜態分析更有效,也是完全可以接受的。

現今的靜態工具還不是萬靈丹,但如果由經驗豐富的程式設計師仔細檢視靜態分析的結果,還是能帶來一定的價值。

另一個很大的幫助來自像 Valgrind 這樣的動態檢查工具。每一個用 C 寫的系統軟體,都應該在每次提交新程式碼時自動用 Valgrind 跑過一遍。

3) 用函式庫來封裝 C。

C 是低階語言,本身沒有內建的安全機制。不過 C 的一個優點是,它允許你在其原始的基礎之上疊加各種抽象層。

一個設計良好的動態字串函式庫就能避免大量的緩衝區溢位問題,而今天幾乎每個像樣的專案都在用。但你還能做得更多。舉例來說,對於可能包含私鑰等敏感資料的關鍵安全程式碼,你可以擴充動態字串函式庫,加入只會在隱含地完成 sanity check 後才從一個緩衝區複製到另一個緩衝區的記憶體複製原語。

此外,如果某個緩衝區含有關鍵資料,你可以設定邏輯上的權限,讓任何試圖從該區域複製資料的行為直接導致程式中止。還有其他透過記憶體管理來更有效地保護重要記憶體分頁、但可攜性較差的方法,不過在現實世界中,考量到可攜性與可預測性,在 C 語言層級做更高層次的保護往往要簡單得多。

總的來說,有很多方法可以探索,避免在毫無防護的情況下使用 C,透過建立一個在它之上進行抽象的函式庫,讓程式設計更安全。

4) 隨機化測試。

單元測試不太容易觸發邊界案例和失敗的 sanity check。有一類已經存在數十年的測試,在我看來還遠遠沒有被充分利用:fuzzy testing(模糊測試)。

這個 OpenSSL 漏洞絕對可以透過發送各種帶有不同隨機參數的 OpenSSL 封包,並搭配像 Valgrind 這樣的動態分析工具來發現。

以我的經驗,大量隨機化測試加上讓程式在 Valgrind 上反覆執行相同測試的環境,能發現不少原本會被忽略的真實世界漏洞。有很多模式可以嘗試,通常你會想要一種注入完全隨機資料的模式,以及介於中間、把合法封包用各種隨機方式破壞的模式。

這項技術的一個典型例子是早期的 DNS 壓縮無窮迴圈漏洞。只要對一個陽春的實作丟幾個隨機封包,幾分鐘內就能找到問題。

5) 改變對安全性與效能的思維。

有趣的是,OpenSSL 之所以自己做配置快取,是因為在某些系統上 malloc/free 很慢。這顯示即使在關鍵的安全程式碼中,效能仍然被看得比安全性還重。在這個具體例子中,必須承認,OpenSSL 開發者包裝 malloc 時大概從未想過這樣做會有什麼安全上的影響。然而,他們會去在意像 *某些* 系統上的配置函式這種低階細節,正顯示出對效能的深切關注,而他們更應該深切關注的是系統的正確性與安全性。

一般來說,無濟於事的是,作為當今伺服器基礎設施實質標準的系統,也就是 Linux,過去擁有、現在也仍然擁有你所能找到最糟的配置器之一,很大程度上是出於授權上的考量,因為更好的配置器不是 GPL 而是 BSD 授權。

這或許又是大企業應該貢獻的另一個領域,透過對 glibc malloc 做出大幅改進。即使有更好的替代方案,glibc malloc 仍然是許多現實世界的系統軟體實際上會使用的東西。

我很希望看到關於 Heartbleed 的討論能採取更務實的態度,因為有一件事是可以肯定的:在這裡或那裡究責,並不會改變 OpenSSL 或其他任何東西實際的安全水準,而且未來還有新的挑戰。舉例來說,HTTP/2.0 的實作在安全性上可能就是一個非常微妙的時刻。

編按:其實是我搞錯了,Glibc 裡面的 malloc 實作是 BSD 授權,所以並非授權問題。我不知道為什麼 Glibc 不改用 Jemalloc,那是一個非常優秀且持續積極開發的配置器。

本文章由 muse-spark-1.2-contributor 進行翻譯

留言