ファジング(fuzzing)は、想定外の入力を自動で大量に与え、ソフトウェアの欠陥を見つけるテスト手法です。仕組みと、自分のソフトで始める方法を解説します(他人のシステムを試す方法は載せません)。
ファジングとは
OWASPは、ファジングを「バグ、脆弱性、想定外の動作を見つけることを目的としたソフトウェアテストの手法」と説明しています。不正な入力、想定外の入力、一部だけ不正な入力を自動で与え、プログラムが異常な動作をしないかを観察します。NISTの用語集(SP 800-95を出典)でも、ファズテストは、ファザーというツールが入力の組み合わせを対象に送り、その反応を調べるものとされています。
特徴は、「このシステムはこう動くはず」という仮定をあまり置かずに試せることです。人間が書くテストは、思いついたケースしか確認できません。ファジングは、思いつかなかった入力で壊れる箇所を探せます。
仕組み
現在の主流は、カバレッジ誘導型(coverage-guided)です。実行中にどのコードを通ったかを計測し、新しいコードに届いた入力を残して、それを変えて次の入力を作ります。ランダムに入力するだけより、深い処理まで届きやすくなります。LLVMのlibFuzzerは、対象のライブラリに組み込んで動かす、カバレッジ誘導型の進化型ファジングエンジンと説明されています。
主な道具
| 道具 | 主な対象 | 特徴 |
|---|---|---|
| libFuzzer | C/C++など(LLVM) | 対象に組み込む型。公式ドキュメントによると、開発元は後継のCentipedeに移っており、重要なバグ修正のみが続く |
| AFL++ | C/C++など | 広く使われるカバレッジ誘導型のファザー |
| Go標準のファジング | Go(1.18以降) | go test -fuzz で実行。見つかった入力は testdata に保存され、通常のテストで再生される |
| OSS-Fuzz | オープンソース全般 | Googleの無料の継続的ファジング基盤。複数のエンジンとサニタイザーを使う |
libFuzzerのファズターゲットは、入力のバイト列とその長さを受け取る関数(LLVMFuzzerTestOneInput)として書きます。Goでは、*testing.F を受け取る FuzzXxx という名前のテスト関数を書き、シード入力を f.Add で追加します。
自分のソフトで始める方法
- 入力を解析する関数を選ぶ(ファイル形式のパーサー、設定の読み込み、プロトコルの処理など)
- その関数に入力を渡すだけの、小さなファズターゲットを書く。速く、毎回同じ結果になる(状態を持ち越さない)ように作る
- 典型的な正常入力を数個、シードとして置く
- AddressSanitizerなどのサニタイザーを有効にして、時間を決めて実行する(Goでは
-fuzztimeで長さを指定できる) - 見つかった入力を再現テストにし、修正後も回帰テストとして残す
- 継続したい場合は、CIで短時間ずつ回すか、オープンソースならOSS-Fuzzへの参加を検討する
Goのファジングでは、1回の実行に1秒の制限があり、カバレッジ(通ったコードの計測)に対応するのはAMD64とARM64です。環境によって制約があるので、公式ドキュメントで確認してください。
自分が管理するソフトだけに行う
ファジングは、大量の不正な入力を送り続ける手法です。許可を得ていない他人のサービスやサーバーに対して行うと、不正アクセスや業務妨害になるおそれがあります。対象は、自分で開発・管理するソフトウェアと、自分の環境に限ります。
できること・できないこと
OWASPは、ファジングは人によるコードレビューやデバッグを置き換えるものではなく、補うものだと述べています。また、見つかるのは比較的単純なバグが多く、ブラックボックスで行うと、見つけた問題の影響の大きさを判断しにくいとしています。
つまり、ファジングは「壊れ方を機械に探させる」のには強い一方、認可の抜けのような設計上の問題は見つけにくい手法です。そのような問題は、IDORのように、仕様に沿った確認のテストで守ります。
当サイトの視点:見つけた入力を回帰テストに残すところまでが一組
ファジングで大事なのは、走らせることよりも、見つけた入力を保存して回帰テストにすることです。Goの標準ファジングが、失敗した入力を testdata に保存して通常のテストで再生するのは、この考え方に沿っています。一度直した問題が、後の改修で戻ってこないようにするためです。入力を解析するコードを持つ人は、まず1つの関数で試すところから始めるのが現実的です。
出典(一次情報)
- OWASP Community「Fuzzing」 — owasp.org(定義、特徴、限界)
- NIST CSRC Glossary「fuzz testing」 — csrc.nist.gov(出典:NIST SP 800-95)
- LLVM「libFuzzer」 — llvm.org(仕組み、ファズターゲット、保守状況)
- Go「Fuzzing」 — go.dev(Go 1.18以降、実行方法、制約)
- Google「OSS-Fuzz」 — google.github.io(概要、対応エンジン)。件数などの数字は変わるため、本文には載せていません。AFL++については、公式ドキュメントで最新の使い方を確認してください