Windows藍屏致損150億美元受災者僅獲賠10美元引熱議
距離Windows 大範圍藍色畫面事件,已經過了6 天。這6 天來,國內外技術網站仍對此事熱議不斷,「罪魁禍首」 CrowdStrike 的名字被頻繁提及,與之伴隨的無一不是質疑和譴責:
CrowdStrike 引發的系統故障導致數千架航班停飛、醫院癱瘓、支付系統崩潰,被專家稱為史上最大的IT 故障。
據Parametrix 保險公司稱,CrowdStrike 錯誤更新引發的全球技術中斷,使美國財富500 強企業(不包括微軟)面臨54 億美元的經濟損失,全球經濟損失總額可能達到150 億美元左右。
基於此,本週CrowdStrike 的股價迅速暴跌超20%。出於對引發此故障的歉意,據悉昨日C rowdStrike 還向其合作方均提供了一張價值10 美元的Uber Eats 禮品卡作為道歉:「為了表達我們的歉意,你的下一杯咖啡或宵夜由我們請客!
除了以上聚焦於CrowdStrike 本身的關注和報道,近日還有一個話題也在開發者圈內引起了不小的討論:」如果CrowdStrike 改用Rust 的話,全球850 萬PC 是不是就不會藍屏了? “
我驚訝地發現,過去幾天發生的所有事情都是由null deref 這樣簡單的錯誤引起的。數十年來,業界一直在使用C++,所有的工具、linters、sanitizers、測試和同儕審查都不足以避免這種情況的發生。因此我在想,如果改用Rust,情況會不會大不相同?
不僅如此,微軟Azure 部門CTO Mark Russinovich 也在事發後轉了一條他發佈於2022 年的推文:「說到語言,現在是時候停止用C/C++ 啟動任何新專案了,請在需要使用非GC 語言的情況下使用Rust。
眼看著不少Rust 狂熱愛好者開始放話“沒錯,Rust 就是唯一答案”,一位同樣喜歡Rust 的資深軟體工程師Julio Merino,在理智地進行了一番全盤分析後得出結論:“就算是Rust,也救不了這次CrowdStrike 的中斷事故。
以下為譯本:
我非常喜歡Rust,也很贊同不應繼續使用C++ 這類記憶體不安全的程式語言,但我還是要說:那些聲稱用Rust 就可以避免上週五全球大面積網路中斷的說法太誇張了,對Rust 的口碑有害無益。
如果CrowdStrike 是用Rust 寫的,那確實可以降低故障的可能性,但它並不能解決導致故障發生的根本原因。所以看到許多人說Rust 是解決這次事故的唯一答案,我就感到非常惱火——這種說法,不僅無法推動Rust 的普及,反而會招來反感:C++ 專家們都知道這次事故的根本原因,看到這種誤導性說法必然不快,從而導致系統程式設計世界的進一步分裂。
那麼,為什麼說Rust 不能解決這個問題呢?接下來我會試著回答這個問題,同時也深入探討這次故障的原因。
故障分析
以下是來自CrowdStrike 官方的「事後分析」:
在2024 年7 月19 日04:09 UTC,作為持續營運的一部分,CrowdStrike 向Windows 系統發布了感測器配置更新。感測器配置更新是Falcon 平台保護機制的持續組成部分。此組態更新觸發了邏輯錯誤,導致受影響的系統崩潰和藍色畫面(BSOD)
導致系統崩潰的感測器配置更新,已於2024 年7 月19 日05:27 UTC 修復。
把上面這段話翻譯為“人話”,就是:
1、CrowdStrike 公司推送了一項配置更新。
2、該更新觸發了「Falcon 平台」中的一個潛在bug。
3. Falcon 中的這個bug 導致了Windows 崩潰。
前兩點並不奇怪:對於任何線上系統來說,變更配置都是“家常便飯”,而這些更新引發程式碼中的bug 也是常見現象。事實上,大多數宕機事件都是由人為配置變更造成的。
顯然,我們應該問問為什麼這個bug 會存在,以及如何修復它以提高產品的穩定性。但我們別忘了第三點:為什麼這個bug 能夠導致整台機器癱瘓?更重要的是,為什麼這個bug 會讓全球如此多的系統宕機?
記憶體錯誤
讓我們從第一個問題開始:Falcon 中的bug 是什麼性質的?
很簡單:在「Channel Files」(又稱設定檔)解析器中存在一個邏輯錯誤,當遇到一些無效輸入時,這段程式碼會試圖存取一個無效的記憶體位置。具體細節並不重要:可能是取消引用空指針,也可能是一般保護故障等等。關鍵在於:崩潰是由無效記憶體存取問題引發的。
這時,一些Rust 狂熱粉可能會跳出來說::「看啊,果然!如果程式碼是用Rust 寫的,這個bug 就不會存在!」我無法否認這個說法:如果用Rust,這個特定的bug確實不會出現。
但那又怎樣?就算避免了這種類型的bug,下一次遇到Rust 也無法避免的bug 時,該宕機還是會宕機——無視Falcon 的本質問題、只關注內存錯誤的行為,好比“只見樹木,不見了森林」。
那麼,Falcon 究竟是什麼呢?
內核崩潰
在我看來,Falcon 是一種“惡意軟體……不過是好人的惡意軟體”,也就是一個終端安全系統。 Falcon 通常安裝在企業機器上,以便安全團隊能夠即時偵測並解除威脅(同時監控員工的行為)。這確實有一定價值:大多數網路攻擊都是透過社會工程手段從入侵企業機器開始的。
這種類型的產品必須對機器有控制權,它必須能夠攔截所有用戶的文件和網路操作以掃描其內容,並且還必須是防篡改的,以防「精明」的企業用戶在閱讀到一些網上修復WiFi 的可疑指導後嘗試停用它,以避免提交IT 工單。
如何實現像Falcon 這樣的產品?最簡單的方法,也是Windows 鼓勵的方法,就是寫一個核心模組。很明顯,Falcon 是一個核心模組,因此它運行在內核空間。這就意味著,Falcon 程式碼中的任何錯誤都可能破壞正在運行的內核,進而導致整個系統崩潰。
我所說的“任何錯誤”,是真的。核心不僅會因為記憶體錯誤而崩潰,也不一定非要「核心崩潰」才能讓機器無法使用:死鎖會讓阻止核心前進,系統呼叫處理程序中的邏輯錯誤會阻止用戶空間之後打開任何文件,一個無限遞歸演算法會耗盡內核的堆疊……破壞內核穩定性的方法實在是太多了,所以我說就算是Rust 也不能完全避免這種事故的發生。
Rust 的記憶體安全性只能解決一種類型的崩潰。另外,Rust 生態系統中對正確性的關注也確實可以最大限度地減少其他類型邏輯錯誤的出現。但是……雖然我們都希望做到完美,但也必須接受錯誤會發生的事實——斷言Rust 是解決問題的唯一答案和堅持使用C++ 一樣,都是不負責任的行為。
要知道,在核心空間工作的C++ 開發者,比了解核心內部結構的Rust 開發者多得多。因此,大部分C++ 開發者都知道這種說法的可笑之處,同時也會增加兩個社群之間的敵意,更是完全違背了讓人們轉向安全語言的這個目標。 Rust 開發者知道Rust 確實可以改善現狀,但C++ 開發者無法接受,因為他們聽到的觀點無法引起他們的共鳴。
從核心空間到使用者空間
還有人說,如果Falcon 不在核心中運行,就根本不會發生這種情況。嗯,這個說法要好一點,但……僅此一點也不一定就能解決問題。
正如我之前提到的,Falcon 需要盡可能防篡改,防止惡意軟體對其進行幹擾,並防止被入侵的用戶試圖停用它。如果惡意軟體或人類能夠輕易做到這一點,那麼這個產品就毫無用處。
現在,Windows 核心完全有能力禁止類似Falcon 的核心模組。相反,核心可以暴露一系列API,讓用戶空間的應用程式可以連接這些API 來提供類似的功能。你知道嗎,微軟確實嘗試讓Windows 朝這個方向發展,但防毒軟體公司威脅要以反壟斷為由起訴,結果整個計畫無疾而終。因此,我們現在只能忍受一個安全性較低的系統,因為防毒軟體公司需要銷售那些煩人的產品。
但是,我們先暫時放下這個麻煩不談。即使Falcon 運行在用戶空間,並透過受控API 與核心通訊…這就足以防止系統故障嗎?請注意,這些API 也需要防篡改。試想一下,如果你希望這個用戶空間驅動程式在核心執行每個二進位檔案之前進行驗證,也就是讓核心在每次執行時都需要從用戶空間驅動程式獲得答案,而這個驅動程式又有問題,那麼系統將無法再執行任何程序。
可如果你讓核心與驅動程式通訊變成可選項,以便核心可以容忍崩潰的驅動程序,那麼就等於給惡意軟體開了一條路,它們可以先嘗試崩潰驅動程序,然後再入侵系統。
因此,僅僅「遷移到用戶空間」顯然也不是解決辦法。
部署中的漏洞
如果我們必須接受bug 的存在,而記憶體相關的bug 並不是唯一會導致系統崩潰的原因,而且將驅動程式移到用戶空間也不是很好的解決方案……那難道就無計可施了?真的沒有辦法防止這種情況發生嗎?
以上我說的這些,都是可以(也應該)採取的措施,以減少系統故障發生的機率,但我們必須接受這樣一個事實:這次代碼bug 只是特定的觸發因素,就算換一個觸發因素也可能會產生類似的惡果。這次全球宕機事件的根本原因,在於配置變更的發布流程。
根據SRE 101(或DevOps,隨便你怎麼叫)規定,配置變更必須分階段進行,以緩慢和受控的方式部署,並在每個步驟進行驗證。這些變更應該先在很小的範圍內進行驗證,然後再向全球推送,而且每次推送都應是漸進的。
考慮到Falcon 的關鍵性以及bug 可能帶來的巨大影響,我很難相信CrowdStrike 沒有對部署進行任何驗證。但根據CrowdStrike 最新更新的事後分析來看,他們確實沒有進行任何形式的測試或金絲雀部署(在將更改推廣到整個服務集群之前,先把更改推廣到一小部分用戶進行測試),這實在是令人難以置信的疏忽。
所以說,CrowdStrike 的部署實踐是造成這次事件的罪魁禍首——也就是說,這次宕機事件是一個流程問題,而不是程式碼或技術問題,改用Rust 也無濟於事。
CrowdStrike 發布初步審查報告,總結:“測試和流程不完善”
誠然如Julio Merino 所說,CrowdStrike 在其官網最新發布了此事件的初步審查報告,並公開了此次事件的整體時間線:
安全故障始於2 月28 日,當時CrowdStrike 開發並分發了一個Falcon 感測器更新,旨在檢測一種新興的、利用Windows 命名管道的攻擊技術,而感測器更新在發布前通過了常規測試。
3 月5 日,更新接受了壓力測試並得到驗證,可以投入使用。因此,當天CrowdStrike 就向使用新的惡意命名管道偵測的客戶分發了快速回應更新。
在4 月8 日-4 月24 日期間,CrowdStrike 又推送了三次使用這種新程式碼模板的快速回應更新,並表示所有這些更新「在生產環境中按預期運行」。
到了7 月19 日,CrowdStrike 又用3 月份的感測器模板發布了兩個快速響應更新,但這次其中一個更新推送的資料格式不正確。然而,CrowdStrike用於檢查內容更新是否按預期運行的驗證系統有問題,它沒有發現這個要推送給所有人的設定檔有錯誤。於是乎,這個本該停止發布的錯誤更新就造成了全球850 萬PC 藍色畫面。
部分網友在看過CrowdStrike 這份冗長的初步審查報告後,精闢總結:「說了這麼多,就是想說我們的測試和流程不完善,不小心把垃圾發佈出來了」;「字數驚人,但歸根結底還是測試程式碼有bug 以及測試不夠」;「不好意思,我們對此更新進行的唯一測試,是一個沒真正通過的自動化測試」。
因此對於這種事故原因,絕對不是改用Rust 就能解決的,根本還是在於測試環節和部署流程的不規範。同時,CrowdStrike 也在事後總結中表示,今後要在發布更新前增加軟體測試,並逐步推出更新,具體補救措施大體分為三個部分:
1、軟體彈性和測試
(1)透過使用以下測試類型改進快速響應內容測試:本地開發人員測試,內容更新和回滾測試,壓力測試、模糊測試和故障注入,穩定性測試和內容介面測試;
(2)在快速反應內容的驗證器中增加額外的驗證檢查;
(3)增強內容解釋器中現有的錯誤處理功能。
2、快速回應內容部署
(1)對快速回應內容實施交錯部署策略,從金絲雀部署開始,再逐步將更新部署到更大的區域;
(2)改善對感測器和系統性能的監控,在快速回應內容部署期間收集回饋訊息,以指導分階段部署;
(3)允許使用者選擇部署的時間和位置,使其能夠更好地控制快速回應內容更新的交付;
(4)透過客戶可訂閱的發布說明提供內容更新詳情。
3、第三方驗證
(1)進行多個獨立的第三方安全代碼審查;
(2)對從開發到部署的端到端品質流程進行獨立審查。