1. 安全地程式設計
1.1. 在一個完整的軟體設計過程中,我們要在建立和審查時就將安全性放在心中,但這只是產品開發過程的開始,接下來是實現、測試、部署、執行、監控、維護,並最終在生命週期結束時將其淘汰
1.2. 開發人員不僅必須忠實地實現一個優良設計中明確的安全規定,還必須避免無意中透過有缺陷的程式碼引入額外的漏洞
1.3. 深思熟慮的設計師可以預測程式設計中遇到的問題,並就注重安全性的領域提供建議等
- 1.3.1. 知道是什麼讓程式碼變得易受攻擊,以及如何讓它更安全
1.4. 在理想情況下,在設計中應該指定主動的安全措施,即為了保護系統、資產和使用者而構建的軟體功能
- 1.4.1. 注重開發中的安全性是為了避免軟體輕易地掉入陷阱,比如元件和工具中的缺陷
2. 挑戰
2.1. 安全編碼的挑戰主要在於避免引入缺陷,因為缺陷有可能成為可被利用的漏洞
- 2.1.1. 避免以不安全的方式進行編碼
2.2. 製作出能夠真正執行的軟體會帶來更高的複雜性,並且需要在設計之外對細節進行填充,所有這些都不可避免地會帶來安全風險
2.3. 完美並不是我們的目標,大多數會導致常見漏洞的編碼失敗模式都很好理解,而且不難糾正
2.4. 惡意影響
-
2.4.1. 在考慮安全編碼時,需要考慮的一個重要因素是瞭解攻擊者可能會怎樣對正在執行的程式碼施加影響
-
2.4.2. 不受信任的輸入可以透過直接或間接兩種方式對程式碼產生影響
-
2.4.3. 有時不受信任的資料與程式碼的互動會觸發錯誤,或者會觸發具有副作用的功能
-
2.4.4. 透過資料對程式碼產生影響的技術被稱為汙染
-
2.4.5. 還有一些其他方法,可以在不儲存資料的情況下,讓輸入資料對程式碼產生間接影響
-
2.4.6. 在大型系統中,當你從攻擊面開始考慮傳遞閉包(即全部路徑的集合)時,你可以體會到滲透到大量程式碼的潛力
-
2.4.6.1. 能夠透過多層進行擴充套件的能力很重要,因為這意味著攻擊者可以訪問比你預期更多的程式碼,從而使其能夠控制程式碼所提供的功能
2.5. 漏洞是bug
-
2.5.1. 大家都已經接受了所有軟體都有bug這件事
-
2.5.1.1. 例外也總是存在的:簡單程式碼、可證明是正確的程式碼,以及執行在航空、醫療或其他關鍵裝置上的高度工程化的軟體
-
2.5.2. 能夠意識到bug的普遍性是走向安全編碼的一個很好的起點
-
2.5.3. 漏洞是對於攻擊者來說有用的一部分軟體bug,攻擊者可以利用漏洞來造成傷害
-
2.5.4. 幾乎無法準確地將漏洞與其他bug區分開,因此一開始可以先識別明顯不是漏洞的bug,也就是完全無害的bug
-
2.5.4.1. 網頁佈局未能按照設計工作,就是一個無害的bug
-
2.5.4.2. 搞亂佈局的bug也可以是有害的
> 2.5.4.2.1. 遮蓋了使用者必須瞭解才能做出準確安全決策的重要資訊,因此漏洞的發現是很複雜的
-
2.5.5. 一般來說我們所有人都在編寫大量的已知錯誤,更不用說未知錯誤了
-
2.5.5.1. 如果還沒有修復全部bug,請考慮那些已知的bug,標記其可能造成的漏洞,並進行修復
-
2.5.5.2. 修復一個bug幾乎總是比調查並證明它是無害的更容易
-
2.5.6. 有些漏洞是難纏的bug,因為它們不符合任何模式,它們不知怎麼躲過了測試,並最終被釋放出來
-
2.5.6.1. 程式碼在正常用途中往往會表現正常,只有在故意進行的攻擊下才會展現出有害的行為
-
2.5.7. 從過去的失敗中吸取教訓是很重要的,因為這些漏洞類別中的很多已經存在了幾十年
2.6. 漏洞鏈
-
2.6.1. 漏洞鏈的意思是看似無害的多個bug可以結合起來,並形成嚴重危害安全的bug
-
2.6.1.1. bug加成效應
-
2.6.2. “墊腳石”bug組合在一起,形成了可被攻擊者利用的漏洞
-
2.6.2.1. 在Pwn2Own駭客大賽中,有一個團隊曾設法將6個bug連線在一起,以實現一次高難度的利用
-
2.6.3. 識別出bug是何時形成漏洞鏈的,通常非常具有挑戰性
-
2.6.3.1. 很容易看出儘可能主動地修復bug是多麼有遠見
-
2.6.3.2. 應該積極修復那些會引入脆弱性的bug,尤其是關鍵資產周圍的bug
-
2.6.3.3. 認為“會沒事的”的觀點就只是一個觀點,而不是證據
-
2.6.3.4. 認為“它永遠不會發生”而將bug留在那裡是有風險的
-
2.6.3.5. 充其量只是一種臨時措施,而不是一個好的最終分流決定
-
2.6.4. 在實踐中,通常很難說服其他人花時間針對那些看似模糊的假設來實施修復,尤其是當修復可疑bug需要大量工作時
-
2.6.5. 很可能大多數大型系統中都充滿了未被發現的漏洞鏈,從而導致我們的系統變脆弱
2.7. bug和熵
-
2.7.1. 一些bug往往會以不可預測的方式對軟體造成破壞,這使得我們很難分析它們的可利用性
-
2.7.2. 由執行執行緒之間的意外互動所引起的bug往往是容易產生這種問題的一類bug,因為它們通常以各種方式出現,並且看起來似乎是隨機的
-
2.7.2.1. 記憶體損壞bug是這類bug中的另一種,因為堆疊的內容在不斷變化
-
2.7.3. 自動化降低了重複嘗試的成本,直到他們的攻擊成功為止
-
2.7.4. 即使你無法清晰地找出明確的因果鏈,熵誘匯出的bug也可能很危險,並值得修復
2.8. 警覺
-
2.8.1. 有了意識,就可以應對困難的挑戰
-
2.8.2. 注意力不集中很容易失敗,即使事情並不困難
-
2.8.3. 如果沒有意識到潛在的安全隱患,並持續關注它,就很容易在不知不覺中掉入它的陷阱
-
2.8.4. 為了能夠交付安全的程式碼,一定要保持警惕並預測所有可能的輸入和事件組合
-
2.8.5. 警覺首先需要的是紀律,但隨著練習的不斷深入,當你知道要注意什麼的時候,它就會成為一種習慣
-
2.8.6. 每一次的修復都避免了未來有可能發生的攻擊
3. 案例
3.1. 2014年,蘋果公司為其大部分產品悄悄地釋出了一系列關鍵安全補丁,並且出於“保護我們的客戶”的考慮,拒絕解釋問題的原因
-
3.1.1. 一個明顯的編輯失誤造成的,這個失誤破壞了安全保護
-
3.1.2. GotoFail
3.2. 教訓
-
3.2.1. 關鍵程式碼中的小錯誤會給安全性帶來毀滅性影響
-
3.2.2. 在預見到的用例中,易受攻擊的程式碼仍能正常工作
-
3.2.3. 對安全性測試來說,測試程式碼拒絕無效用例的能力,比測試程式碼在合法用例中的正常執行更為重要
-
3.2.4. 程式碼審查非常重要,它能夠找出無意間引入的bug
3.3. 對策
-
3.3.1. 更好的測試
-
3.3.1.1. 至少應該為每個if條件都設定一個測試用例,以確保所有必要的檢查都有效
-
3.3.2. 注意那些無法訪問的程式碼
-
3.3.2.1. 很多編譯器都提供了選項以對此進行標記
-
3.3.3. 讓程式碼儘可能明確,比如多用圓括號和花括號,即使可以略去它們
-
3.3.4. 使用諸如linter之類的原始碼分析工具,可以提高程式碼質量,並且在此過程中可能會標記出一些潛在的漏洞,以提前進行修復
-
3.3.5. 考慮使用ad hoc原始碼過濾器來檢測可疑模式
-
3.3.6. 測量並要求全面的測試覆蓋率,尤其是對於安全關鍵程式碼
4. 編碼漏洞
4.1. 新類別的bug是無窮無盡的,不要徒勞地試圖編寫一份涵蓋所有潛在軟體漏洞的完整列表
4.2. 原子性
-
4.2.1. 將任務作為一個單一的步驟並保證有效地完成
-
4.2.2. 原子性是一個重要的防禦武器,它可以用來預防可能會導致漏洞的意外情況
4.3. 時序攻擊
-
4.3.1. 時序攻擊是一種旁路攻擊,它會從操作執行的時間上推斷出一些資訊,以此間接地瞭解系統中本應是私密的一些狀態
-
4.3.2. 時間差有時可以提供一些暗示,也就是說會有少量的受保護資訊被洩露,從而使攻擊者受益
-
4.3.3. Meltdown(熔斷)和Spectre(幽靈)是針對現代處理器的時序攻擊,它們執行在軟體層面之下,但原理是相同的
-
4.3.3.1. 利用了預測執行(speculative execution)的行為特徵,即處理器會加速得到預計算結果,同時為了速度而暫時放鬆各種檢查
-
4.3.3.2. 攻擊程式碼可以透過檢查快取的狀態,來推測被取消的預測執行期間發生的事情
-
4.3.3.3. 記憶體快取技術加快了執行速度,但快取不會直接披露給軟體
-
4.3.3.4. 程式碼可以判斷特定記憶體位置的內容是否曾經存在於快取中,這是透過測量記憶體訪問時間做出的判斷,因為被快取的記憶體處理速度快得多
-
4.3.4. 當軟體中存在一系列緩慢的操作(想想if…if…if…if…)時,軟體的執行就會出現時間差
-
4.3.4.1. 當我們對執行中的事件順序有所瞭解時,就可以透過時間差來推斷有價值的資訊
-
4.3.4.2. 當使用同一臺機器上執行的程式碼來利用Meltdown和Spectre時,亞毫秒級的時間差都是可以被注意到的,並且這也是很重要的
-
4.3.5. 最好的緩解措施是將時間差縮小到可接受的水平,也就是難以察覺的水平
-
4.3.6. 當軟體中存在固有的時間差,並且這個時序旁路會導致嚴重的洩露時,你可以用來緩解風險的做法就是人為引入時延,以模糊時序訊號
4.4. 序列化
-
4.4.1. 序列化是指將資料物件轉換為位元組流的通用技術
-
4.4.2. 攻擊者不僅能夠篡改關鍵資料值,而且透過構造無效的位元組序列,甚至能夠使反序列化後的程式碼執行有害的操作
-
4.4.2.1. 需要信任輸入的資料,才可以構建出相應的物件以完成相應的功能
-
4.4.3. 只有對受信任的序列化資料執行反序列化才是安全的