【Go】Go1.18で学ぶFuzzing入門
Go1.18ではGenericsが最も注目されていますが、同じくfuzzing(testing.F)が採用される事になりました。(正確にはGo 1.18Beta1から)
個人的にはfuzzingという手法自体を初めて知り、 手軽で面白いなと感じたのでfuzzingとはどの様なものなのかを含め纏めておきます。
Fuzzingとは
ファジング(英語:fuzzing)とは、コンピュータプログラムヘの入力として、無効なデータ、予期しないデータ、ランダムなデータを用いた自動化ないし半自動化されたソフトウェアテストの手法である。コンピュータプログラムにファズ(「予測不可能な入力データ」の意、英語: fuzz))を与えることで、意図的に例外的な状況を発生させ、その例外的な状況での挙動を確認するという方法を用いる。
出典: フリー百科事典『ウィキペディア(Wikipedia)』
fuzzingはブラックボックス(あるいはグレーボックス)テストの一種で、プログラムに様々な入力を行うことで予期せぬ挙動が起こらないかを調べる手法である。
fuzzingを行うツールの事をファザー(fuzzer)と呼ぶ。
主にセキュリティやロバストネスチェックに使われ、プログラムの脆弱性を明らかにするために活用されることが多い。 テストの特性上、バグが存在しないことではなく、バグが存在することを示すために使用される点に注意が必要です。(テストでエラーが検出されなくても、問題が発生する可能性はある)
現在あるサービスでも多く活用され実績があり、 Google ChromeやMicrosoft Edge、Internet Explorerなどでも大規模なfuzzingテストが実施され、かなりの数のバグが発見されている。
用語
- fuzz 入力されるデータ、様々なfuzzによってテストを行うことでエラーを探す
- fuzzer fuzzingを行うツール、商用やOSSなど様々なfuzzerが存在する
- corpus fuzzのデータセット群 複数のfuzzを入力するが、多くのfuzzerは対象関数のテストのカバレッジ情報を取得しており、カバレッジを上げられる入力が得られるとcorpusに登録し、テストを網羅的にしていく。
活用目的
バグの発見、静的解析、テストケースの削減、メモリリークの検出、Webブラウザのセキュリティ診断 など
代表的なテスト対象
コマンドライン、環境変数、ファイル形式、ネットワーク・プロトコル、Webアプリケーション など
有名なFuzzer
商用やOSSのFuzzerが存在する。
など
Goでのfuzzing
Go1.18より前に存在しているgo-fuzzはAFLをベースに開発され、標準パッケージで100以上、golang.org/x/パッケージで40以上、
その他を含めると300以上のバグを発見するという実績を残している。
参照:https://github.com/dvyukov/go-fuzz#trophies
これらの実績があり、標準ライブラリにfuzzingを採用する流れのようです。
fuzzとcorpus
testing.Fにおいて、テストする関数の引数(fuzz)は現在、以下のプリミティブ型のものに絞られます。
- string, []byte
- int, int8, int16, int32/rune, int64
- uint, uint8/byte, uint16, uint32, uint64
- float32, float64
- bool
fuzzingには様々なアルゴリズムがあるようだが、
go-fuzzでは、ランダムに生成するfuzzにはランダムな値を生成するのではなく、正常な引数の一部を変更し、ランダムな値を生成して利用する。
AST(抽象構文木)を使い、対象関数のテストのカバレッジ情報を取得し、カバレッジを上げる様な入力があればcopusに登録することで網羅的なテストを行っている。
標準パッケージのtesting.Fでも、Design Draft: First Class Fuzzing によると、
FuzzingEngineがcopusから新たなcopusを生成し、成長させながらテストを行う仕組みとなっている。
一度利用したfuzzをキャッシュとしてtestdata/corpus/FuzzTarget 配下および $GOCACHE/fuzz 配下に保存し、再利用され、
go-fuzzと同様、カバレッジ情報を考慮しているので優れたseed copusを保持していると効果的なテストができます。
詳細な内容については言及されていないようでした。
fuzzingの実行
主にfuzzerでは事前に用意したfuzzを実行し終わるまでテストを回したり、
テスト時間を指定してfuzzを生成し続け、エラーが発生しないことを確認することが一般的です。
処理性能やcopusのキャッシュ、エラーログのためのデータ容量を圧迫するので注意が必要です。
参照
公式ドキュメント
実際の使い方については公式から丁寧なtutorialが公開されているので、こちらを参照してください。
- Tutorial: Getting started with fuzzing - The Go Programming Language