這根本不是 BASH 的 BUG!
在前幾天看到 CVE-2014-6277 這 個 BASH 的 bug 的時候,我就覺得挺奇葩的:這這種明顯是有意實現的功能怎么會存在這么大的安全隱患呢?我不是專門搞安全的,同時覺得,這個 bug 可能雖然影響廣泛,但并不是什么很有技術含量的利用思路。所以我就把這個事情放下沒去想它。然而,隨之而來的國內國外的各種媒體宣傳、安全專家的聯名建 議、茶余飯后的坊間暢談……對于 BASH 的這個 bug 無人不認為是個大 bug。不少人為此還在幸災樂禍……但是我卻沒從各種評論中找到我的問題的答案。
但是,當我看到這篇隨筆時,我不得不贊同作者的觀點:這根本不是 BASH 的 bug!
所以我將原文翻譯至此,希望能夠帶來一些思考。
也許我們今天看到的一個愚蠢的 bug,在歷史上的某一天,是一個有意而為之的神奇特性。也許我們應該思考的不僅僅是這一刻的 bug 或者安全隱患本身,而是在軟件項目這個極具工程和創作品雙重特性的活動中,如何有效的保證某個特性不會變成 bug。所以什么規范了,文檔了,可真得不是紙上談兵!
不過話說回來,無論如何,我仍然堅信:“Less is exponentially more!(大道至簡)”少一點 Feature 或許就是少一點 Bug 呢?
————翻譯分隔線————
我想要討論一下,關于這個 BASH 的安全問題其實不是一個 bug。這顯然是一個特性。誠然,這是一個被錯誤實現并被錯誤使用的特性。不過它仍然是個特性。
問題在于它是在 25 年前設計的。Apache 在那五年之后才出現!沒錯,那個時候互聯網已經有了,不過那是個親密無間的小圈子。在那時,互聯網軟件和協議的安全根本不在考慮之列,更不用說開發像 shell 這樣的程序的時候了。
因此,我們討論的上下文是設計一個 UNIX shell,其子進程用非正式且常見的方式創建和運行。當創建一個新的子進程,環境變量可以用于從父進程向子進程傳遞某些數據。在 BASH 的情況里,這是一個需求提到的特性,在 BASH 父進程中定義的函數可以傳遞給子進程并獲得定義。使用環境變量傳遞這些函數的定義是自然而然的事情。
而在環境變量傳遞的函數定義之后可以執行任何命令,就是那個略微超出設計規格的實現了。由于大部分情況下 BASH 程序在環境變量中存放的是要傳遞的函數,所以通常不會有其他命令。
不過在 BASH 被設計的時候,一個用戶是否能添加命令到這樣的環境變量中,會被認為是一個特性。在任何情況下,子進程的行為依賴于配置這個環境變量的用戶,所以沒有任何關于安全方面的擔憂。
這個特性被記錄在用于導出內建命令的 -f 選項中。而使用內容以“() {”開始的環境變量在函數定義后可以包含更多的命令的實現細節并沒有文檔記錄,不過仍然可以認為是一個特性。
問題在于,五年后,新的使用 BASH 作為子進程,并仍然使用環境變量傳遞數據的軟件被開發出來(Apache,DHCP 等等)。不幸的是,一些數據不再是來自可信的本地系統用戶(譯注:典型的物理安全信任),而是來自這個星球上的,互聯網上任意的用戶或者程序。同時,沒有 文檔記錄的(但已經被發布的)BASH 的特性卻被遺忘了。
這是一個關于 UNIX 系統的古老觀念,環境變量可以由父進程控制,還有更古老、更通用的觀念是輸入的數據應當是合法的。
大概像 Apache 這樣的程序都對環境變量進行了正確的過濾。但不幸的是,它們在正確校驗輸入的數據時失敗了,因為它們并未考慮到以“() {” 開始的數據,將會被它們產生的 BASH 子進程解釋。如果這里有 bug 的話,也不是在 BASH 中,而是在 Apache 以及其他面向網絡的程序,沒有正確的驗證和控制它們傳遞給 BASH 的數據。
也就是說,BASH 有自己的問題,只不過跟大多數軟件一樣:它缺少一個規范的文檔,并且擁有沒有記錄的特性。
我們的問題不在于 BASH bug,這基本上與 Ariane 5 的 bug 類似:使用來自早期的系統中,規范已經過期的模塊。
在該特定情況中,使用過期的規范的原因似乎是由于沒有任何規范被記錄下來,并且對于相關的實現細節也沒有文檔。但從另一方面來說,這是自由軟 件,因此查閱源代碼的難度就跟在臉上找鼻子一樣容易。當復用一個沒有規范和文檔的模塊時,檢查源代碼的實現應當是一個標準流程,不過顯然 Apache 或 DHCP 的開發者沒有這么做。
bug 還在那里,就像 Ariane 5 的 bug 不是 Ariane 4 的 bug 一樣!