#90DaysOfDevOps - 模糊測試 (Fuzzing) - Day 16
模糊測試(Fuzzing)
模糊測試(Fuzzing),也稱為「模糊測試」(fuzz testing),是一種軟體測試技術,涉及向電腦程式提供無效、意外或隨機的資料作為輸入。 模糊測試的目標是透過使程式崩潰或表現出非預期的行為來識別程式中的安全漏洞和其他錯誤。
模糊測試可以手動執行,也可以使用測試函式庫或框架來為我們製作輸入。
為了更好地理解模糊測試,讓我們來看一個例子,想像這段程式碼:
func DontPanic(s string) {
if len(s) == 4 {
if s[0] == 'f' {
if s[1] == 'u' {
if s[2] == 'z' {
if s[3] == 'z' {
panic("error: wrong input")
}
}
}
}
}
}
這是一個接受 string 作為唯一參數的 Go 函式。
查看函式,看起來這個函式只會在一種情況下 panic(例如崩潰)——如果提供的輸入是單字 fuzz。
顯然,這個函式非常簡單,我們只需查看它就能看到它的行為。 然而,在更複雜 的系統中,這類失敗點可能不明顯,可能會被測試它的人或編寫單元測試案例的人遺漏。
這就是模糊測試派上用場的地方。
Go 模糊測試函式庫(自 Go 1.18 起成為標準語言函式庫的一部分)為測試案例生成許多輸入,然後根據覆蓋率和結果確定哪些輸入是「有趣的」。
如果我們為這個函式編寫模糊測試,會發生以下情況:
- 模糊測試函式庫將開始提供隨機字串,從較小的字串開始,逐漸增加它們的大小。
- 一旦函式庫提供長度為 4 的字串,它會注意到測試覆蓋率的變化(
if (len(s) == 4)現在為true),並將繼續生成此長度的輸入。 - 一旦函式庫提供以
f開頭的長度為 4 的字串,它會注意到測試覆蓋率的另一個變化(if s[0] == "f"現在為true),並將繼續生成以f開頭的輸入。 - 對於
u和雙z會重複相同的事情。 - 一旦它提供
fuzz作為輸入,函式將 panic,測試將失敗。 - 我們已經成功進行了模糊測試!
模糊測試的另一個良好實踐是保存使程式碼崩潰的輸入,並每次都執行它們,以確保我們透過模糊測試捕獲的原始錯誤永遠不會再次引入我們的程式碼中。
這也可以是模糊測試框架的一個功能。
當模糊測試不夠時
模糊測試是一種有用的技術,但在某些情況下它可能沒有幫助。
例如,如果使我們的程式碼失敗的輸入過於具體,並且沒有線索可以幫助,模糊測試函式庫可能無法猜測它。
如果我們將前一段的範例程式碼更改為如下內容:
func DontPanic(s input) {
if (len(s) == 4) && s[0] == 'f' && s[1] == 'u' && s[2] == 'z' && s[3] == 'z' {
panic("error")
}
}
或者只是:
func DontPanic(s input) {
if s == "fuzz" {
panic("error")
}
}
那麼模糊測試將無法幫助我們,因為它生成確切字串 fuzz 的機會很小,而且沒有任何線索,
並且在前一種情況下觸發程式碼覆蓋率變化的輸入(長度為 4 的字串、以 z 開頭的長度為 4 的字串等)現在都不會觸發程式碼覆蓋率(因為我們只有一個 if 檢查,與前一個範例中的 5 個相比)。
因此,重要的是要理解,雖然模糊測試是檢測程式碼中異常和邊緣情況的好方法,但它不是 100% 正確程式碼的萬靈藥。
實用範例
如果您想親自動手使用 Go 進行模糊測試,請查看我在該主題上的範例儲存庫。
它包含我在本文中使用的範例 + 一個觸發失敗的模糊測試,以及如何自己執行測試的說明。
資源
- Fuzzing - Wikipedia
- Fuzzing in Go by Valentin Deleplace, Devoxx Belgium 2022
- Write applications faster and securely with Go by Cody Oss, Go Day 2022
請參閱 Day 17。