想寫無Bug的安全程式碼?看防禦性程式設計的藝術

一杯雜湊不加鹽發表於2017-02-21

為什麼開發者不編寫安全的程式碼?我們在這並不是要再一次討論「整潔程式碼」。我們要從純粹的實用觀點出發,討論軟體的安全性和保密性。是的,因為不安全的軟體不僅無用,而且還可怕。我們來看看什麼是不安全的軟體。

  • 1996年6月4日,歐洲航天局的 Ariane 5 Flight 501 在起飛後 40 秒被引爆。因為導航軟體裡的一個 bug,這個價值 10 億美金的運載火箭不得不自毀。
    想寫無Bug的安全程式碼?看防禦性程式設計的藝術
  • 1991年2月25日,MIM-104 Patriot(愛國者)裡的一個軟體錯誤使它的系統每一百小時有三分之一秒的時鐘偏移,導致定位攔截入侵導彈失敗。結果伊拉克的飛毛腿導彈擊中宰赫蘭(沙烏地阿拉伯東北部城市)的一個美軍軍營,28 人死亡,100 多人受傷。
  • 其他案例,請參見《Bug 引發的 18 次重大事故》。

這應該能夠說明編寫安全軟體的重要性了,尤其在特定的環境中。當然也包括其他用例中,我們也應該意識到我們的軟體 bug 會導致什麼後果。

防禦性程式設計初窺

為什麼我認為在特定種類的工程中,防禦性程式設計是解決這些問題好辦法?

抵禦那些不可能的事,因為看似不可能的事也會發生。

防禦性程式設計中有很多防禦方式,這也取決於你的軟體專案所需的「安全」級別和資源級別。

防禦性程式設計是防禦式設計的一種形式,用來確保軟體在未知的環境中能繼續執行。防禦性程式設計的實踐往往用於需要高可用性、安全性、保密性的地方。—— 維基百科

我個人相信這種方法適合很多人蔘與的大型、長期的專案。例如,一個需要大量維護的開源專案。

我們來探索一下我提出的關鍵點,來完成一個防禦性程式設計的實現。

永遠不要相信使用者輸入

設想你總是獲取到你不想要的東西。因為像我們說過的,我們預期的是異常情況的出現,(所以)要時刻防備使用者輸入以及通常會傳入你係統的東西,這是你成為一個防禦性程式設計師的方法。試著做到儘可能的嚴格,確保輸入的值就是你所期望的值。

想寫無Bug的安全程式碼?看防禦性程式設計的藝術

進攻是最好的防守

設定白名單而不是黑名單。舉個例子,當你驗證影像副檔名時,不要檢查非法的型別,而是檢查合法的型別並排除其他型別。在 PHP 有無數的開源校驗庫可以讓你的工作變簡單。

進攻是最好的防守。共勉

資料庫抽象化

OWASP Top 10 Security Vulnerabilities 排首位的是注入攻擊。這意味著有些人(很多人)還沒有使用安全的工具來查詢資料庫。請使用資料庫抽象包或庫。在 PHP 裡你可以使用 PDO確保基本的注入攻擊防範

不要重複發明輪子

你不用框架(或微框架)嗎?好吧恭喜你,你喜歡毫無理由地做額外的工作。這並不僅跟框架有關,也意味著你可以方便地使用已經存在的、經過測試的、受萬千開發者信任的、穩定的新特性,而不是你只為了自己從中受益而製作的東西。你自己建立方法的唯一原因是你需要的東西不存在,或存在但不符合你的需求(效能差、缺失特性等等)。

這就是所謂的智慧程式碼重用。擁抱它吧。

不要相信開發者

防禦性程式設計與防禦性駕駛相關聯。在防禦性駕駛中,我們假設周圍的每個人都可能犯錯。所以我們要留意別人的行為。相同概念也適用於防禦性程式設計,我們作為開發者不要相信其他開發者的程式碼。我們同樣也不要相信我們的程式碼。

在很多人蔘與的大型專案中,我們有許多方式編寫並組織程式碼。這也導致混亂甚至更多的 bug。這也是我們需要加強規範程式碼風格和程式碼檢查的原因,讓生活更輕鬆。

編寫符合 SOLID 原則的程式碼

這是(防禦性)程式設計最困難的部分——編寫不糟糕的程式碼。這也是很多人知道並一直在討論的,但沒有人真正關心或將注意力和精力放在實現符合 SOLID 原則的程式碼上。

讓我們看一些糟糕的例子

避免:未初始化的屬性

在這個例子中,我們需要牢記簽發付款前要先呼叫 setCurrency。這是很糟糕的事情,一個像這樣的改變狀態的操作(簽發付款)不應該分兩步來完成,且使用兩個公開的方法。我們還可以用很多方法付款,但我們必須只有一個公開的方法來改變狀態(物件不應該存在不一致的狀態)。

在這個例子中,我們把它改進,將未初始化的屬性封裝進 Money 物件。

使它萬無一失。不要使用未初始化的物件屬性。

避免:在類的作用域外洩露狀態

在這個例子中,Message 是通過引用傳遞的,兩個例項的輸出都是 “joe message”。一個解決方案是複製 Mailer 建構函式中的 message 物件。但是我們應該做的是試著使用(不可變的)值物件,而不是簡單可變的 Message 物件。儘可能使用不可變的物件。

編寫測試

這點我們很還需要再說嗎?編寫單元測試可以幫助你秉承一般的原則,比如高內聚、單一職責、低耦合和正確的物件組合。它不僅幫助你測試小的單元用例,也能測試你組織物件的方式。確實,當測試你的小功能時,你會清晰的看到你需要測試多少情況和需要模擬多少物件,來達到 100% 的覆蓋率。

結論

希望你喜歡這篇文章。記住這些僅僅是建議而已,由你決定何時、何處以及是否應用。

感謝閱讀!

打賞支援我翻譯更多好文章,謝謝!

打賞譯者

打賞支援我翻譯更多好文章,謝謝!

任選一種支付方式

想寫無Bug的安全程式碼?看防禦性程式設計的藝術 想寫無Bug的安全程式碼?看防禦性程式設計的藝術

相關文章