Golang Fuzzing is Beta Ready
近日 Golang 團(tuán)隊(duì)宣布,go 原生支持的模糊測(cè)試 (fuzzing) 的 beta 版本在 development 分支上已經(jīng)可以使用了。開發(fā)人員可以使用該特性為項(xiàng)目構(gòu)建 fuzzing 測(cè)試。
那么先來(lái)看下什么是 Fuzzing 呢?
什么是 Fuzzing?
Fuzz 本意是“羽毛、細(xì)小的毛發(fā)、使模糊、變得模糊”,后來(lái)用在軟件測(cè)試領(lǐng)域,中文一般指“模糊測(cè)試”,英文有的叫“Fuzzing”,有的叫“Fuzz Testing”。本文用 fuzzing 表示模糊測(cè)試。
Fuzzing 技術(shù)可以追溯到 1950 年,當(dāng)時(shí)計(jì)算機(jī)的數(shù)據(jù)主要保存在打孔卡片上,計(jì)算機(jī)程序讀取這些卡片的數(shù)據(jù)進(jìn)行計(jì)算和輸出。如果碰到一些垃圾卡片或一些廢棄不適配的卡片,對(duì)應(yīng)的計(jì)算機(jī)程序就可能產(chǎn)生錯(cuò)誤和異常甚至崩潰,這樣,Bug 就產(chǎn)生了。所以,F(xiàn)uzzing 技術(shù)并不是什么新鮮技術(shù),而是隨著計(jì)算機(jī)的產(chǎn)生一起產(chǎn)生的古老的測(cè)試技術(shù)。
Fuzzing 技術(shù)是一種基于黑盒(或灰盒)的測(cè)試技術(shù),通過(guò)自動(dòng)化生成并執(zhí)行大量的隨機(jī)測(cè)試用例來(lái)發(fā)現(xiàn)產(chǎn)品或協(xié)議的未知漏洞。隨著計(jì)算機(jī)的發(fā)展,F(xiàn)uzzing 技術(shù)也在不斷發(fā)展。
Fuzzing有用么?
Fuzzing 是模糊測(cè)試,顧名思義,意味著測(cè)試用例是不確定的、模糊的。
計(jì)算機(jī)是精確的科學(xué)和技術(shù),測(cè)試技術(shù)應(yīng)該也是一樣的,有什么的輸入,對(duì)應(yīng)什么樣的輸出,都應(yīng)該是明確的,怎么會(huì)有模糊不確定的用例呢?這些不確定的測(cè)試用例具體會(huì)有什么作用呢?
為什么會(huì)有不確定的測(cè)試用例,我想主要的原因是下面幾點(diǎn):
1、我們無(wú)法窮舉所有的輸入作為測(cè)試用例。我們編寫測(cè)試用例的時(shí)候,一般考慮正向測(cè)試、反向測(cè)試、邊界值、超長(zhǎng)、超短等一些常見(jiàn)的場(chǎng)景,但我們是沒(méi)有辦法把所有的輸入都遍歷進(jìn)行測(cè)試的。
2、我們無(wú)法想到所有可能的異常場(chǎng)景。由于人類腦力的限制,我們沒(méi)有辦法想到所有可能的異常組合,尤其是現(xiàn)在的軟件越來(lái)越多的依賴操作系統(tǒng)、中間件、第三方組件,這些系統(tǒng)里的bug或者組合后形成的 bug,是我們某個(gè)項(xiàng)目組的開發(fā)人員、測(cè)試人員無(wú)法預(yù)知的。
3、Fuzzing 軟件也同樣無(wú)法遍歷所有的異常場(chǎng)景。隨著現(xiàn)在軟件越來(lái)越復(fù)雜,可選的輸入可以認(rèn)為有無(wú)限個(gè)組合,所以即使是使用軟件來(lái)遍歷也是不可能實(shí)現(xiàn)的,否則你的版本可能就永遠(yuǎn)也發(fā)布不了。Fuzzing 技術(shù)本質(zhì)是依靠隨機(jī)函數(shù)生成隨機(jī)測(cè)試用例來(lái)進(jìn)行測(cè)試驗(yàn)證,所以是不確定的。
這些不確定的測(cè)試用例會(huì)起到我們想要的測(cè)試結(jié)果么?能發(fā)現(xiàn)真正的 Bug 么?
1、Fuzzing 技術(shù)首先是一種自動(dòng)化技術(shù),即軟件自動(dòng)執(zhí)行相對(duì)隨機(jī)的測(cè)試用例。因?yàn)槭且揽坑?jì)算機(jī)軟件自動(dòng)執(zhí)行,所以測(cè)試效率相對(duì)人來(lái)講遠(yuǎn)遠(yuǎn)高出幾個(gè)數(shù)量級(jí)。比如,一個(gè)優(yōu)秀的測(cè)試人員,一天能執(zhí)行的測(cè)試用例數(shù)量最多也就是幾十個(gè),很難達(dá)到 100 個(gè)。而 Fuzzing 工具可能幾分鐘就可以輕松執(zhí)行上百個(gè)測(cè)試用例。
2、Fuzzing 技術(shù)本質(zhì)是依賴隨機(jī)函數(shù)生成隨機(jī)測(cè)試用例,隨機(jī)性意味著不重復(fù)、不可預(yù)測(cè),可能有意想不到的輸入和結(jié)果。
3、根據(jù)概率論里面的“大數(shù)定律”,只要我們重復(fù)的次數(shù)夠多、隨機(jī)性夠強(qiáng),那些概率極低的偶然事件就必然會(huì)出現(xiàn)。Fuzzing 技術(shù)就是大數(shù)定律的典范應(yīng)用,足夠多的測(cè)試用例和隨機(jī)性,就可以讓那些隱藏的很深很難出現(xiàn)的Bug成為必然現(xiàn)象。
目前,F(xiàn)uzzing 技術(shù)已經(jīng)是軟件測(cè)試、漏洞挖掘領(lǐng)域的最有效的手段之一。Fuzzing 技術(shù)特別適合用于發(fā)現(xiàn) 0 Day 漏洞,也是眾多黑客或黑帽子發(fā)現(xiàn)軟件漏洞的首選技術(shù)。Fuzzing 雖然不能直接達(dá)到入侵的效果,但是 Fuzzing 非常容易找到軟件或系統(tǒng)的漏洞,以此為突破口深入分析,就更容易找到入侵路徑,這就是黑客喜歡 Fuzzing 技術(shù)的原因。
想要了解更多特性可以參考 golang fuzzing 的設(shè)計(jì)草案 draft-fuzzing[1]
快速上手
$ go get golang.org/dl/gotip
$ gotip download dev.fuzz
這將從 dev.fuzz 開發(fā)分支中構(gòu)建 Go Toolchain,等將來(lái)代碼合并到 Master 分支后就不需要自己構(gòu)建了。
gotip test -fuzz=FuzzFoo
dev.fuzz 分支中會(huì)有正在進(jìn)行的開發(fā)和 bug fix,需要每次運(yùn)行 gotip download dev.fuzz 以拉取最新的代碼。
為了保障代碼兼容性,在提交包含模糊測(cè)試的源文件時(shí)需要使用 gofuzzbeta 構(gòu)建 tag。默認(rèn)情況下在 dev.fuzz 分支中的構(gòu)建時(shí)已經(jīng)啟用此 tag。
// +build gofuzzbeta
Fuzz 測(cè)試樣例
Fuzz 測(cè)試與普通的單元測(cè)試類似,需要在 *_test.go 中定義 Fuzzxxx 函數(shù)。此函數(shù)必須傳遞*testing.f 參數(shù),就像 * testing.t 參數(shù)傳遞給 testxxx 函數(shù)一樣。
下面是測(cè)試 net/url 的 fuzzing target 的示例。
// +build gofuzzbeta
package fuzz
import (
"net/url"
"reflect"
"testing"
)
func FuzzParseQuery(f *testing.F) {
f.Add("x=1&y=2")
f.Fuzz(func(t *testing.T, queryStr string) {
query, err := url.ParseQuery(queryStr)
if err != nil {
t.Skip()
}
queryStr2 := query.Encode()
query2, err := url.ParseQuery(queryStr2)
if err != nil {
t.Fatalf("ParseQuery failed to decode a valid encoded query %s: %v", queryStr2, err)
}
if !reflect.DeepEqual(query, query2) {
t.Errorf("ParseQuery gave different query after being encoded\nbefore: %v\nafter: %v", query, query2)
}
})
}
可以通過(guò)下面的命令使用 Go Doc 閱讀有關(guān) Fuzz API 的更多信息。
gotip doc testing
gotip doc testing.F
gotip doc testing.F.Add
gotip doc testing.F.Fuzz
須知
模糊測(cè)試會(huì)消耗大量?jī)?nèi)存,并在運(yùn)行時(shí)可能會(huì)影響機(jī)器的性能。 go test -fuzz默認(rèn)使用$ gomaxProcssor個(gè)進(jìn)程并行運(yùn)行模糊測(cè)試, 可以在運(yùn)行Go Test時(shí)加上--parallel來(lái)限制并發(fā)使用 cpu 的數(shù)量。要注意目前沒(méi)有限制寫入 fuzzing 緩存的文件數(shù)或總字節(jié)數(shù),因此它可能占據(jù)大量存儲(chǔ)(即幾個(gè)GB),可以通過(guò)運(yùn)行 gotip clean -fuzzcache來(lái)清除模糊測(cè)試后的緩存。
最后
這個(gè)特性并不會(huì)出現(xiàn)在即將發(fā)布的 Go1.17 中,但是計(jì)劃在未來(lái)的某個(gè) Go 版本中正式 release 這個(gè)特性。官方希望這個(gè)特性能夠幫助 Go 開發(fā)人員開始編寫 fuzzing 測(cè)試,并提供關(guān)于 fuzzing 的設(shè)計(jì)的反饋,為未來(lái)該功能的代碼正式合并 master 做準(zhǔn)備。
Happy fuzzing!
原文地址[2]
參考資料
draft of fuzzing: https://go.googlesource.com/proposal/+/master/design/draft-fuzzing.md
[2]fuzz-beta: https://blog.golang.org/fuzz-beta
