用語辞典8分で読める

ファジングとは:でたらめな入力を大量に与えて、自分のソフトの欠陥を見つける手法

ファジングは、想定外の入力を自動で大量に与えてバグや脆弱性を見つけるテスト手法です。仕組みと、自分のソフトでの始め方を解説します。

本文を読む
公開日
更新日
目次

ファジング(fuzzing)は、想定外の入力を自動で大量に与え、ソフトウェアの欠陥を見つけるテスト手法です。仕組みと、自分のソフトで始める方法を解説します(他人のシステムを試す方法は載せません)。

ファジングとは

OWASPは、ファジングを「バグ、脆弱性、想定外の動作を見つけることを目的としたソフトウェアテストの手法」と説明しています。不正な入力、想定外の入力、一部だけ不正な入力を自動で与え、プログラムが異常な動作をしないかを観察します。NISTの用語集(SP 800-95を出典)でも、ファズテストは、ファザーというツールが入力の組み合わせを対象に送り、その反応を調べるものとされています。

特徴は、「このシステムはこう動くはず」という仮定をあまり置かずに試せることです。人間が書くテストは、思いついたケースしか確認できません。ファジングは、思いつかなかった入力で壊れる箇所を探せます。

仕組み

① 種になる入力(シード)を少し用意する
② ファザーが入力を少しずつ変え、対象の関数に渡す
↓ 実行して、通ったコードと挙動を観察
③ クラッシュや異常があれば入力を保存、新しい経路に届いた入力は②へ戻す
入力を作る、実行する、異常を見る、の繰り返し。カバレッジ誘導型は、新しい経路に届いた入力を残して次の入力に使う。

現在の主流は、カバレッジ誘導型(coverage-guided)です。実行中にどのコードを通ったかを計測し、新しいコードに届いた入力を残して、それを変えて次の入力を作ります。ランダムに入力するだけより、深い処理まで届きやすくなります。LLVMのlibFuzzerは、対象のライブラリに組み込んで動かす、カバレッジ誘導型の進化型ファジングエンジンと説明されています。

主な道具

道具主な対象特徴
libFuzzerC/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 で追加します。

自分のソフトで始める方法

  1. 入力を解析する関数を選ぶ(ファイル形式のパーサー、設定の読み込み、プロトコルの処理など)
  2. その関数に入力を渡すだけの、小さなファズターゲットを書く。速く、毎回同じ結果になる(状態を持ち越さない)ように作る
  3. 典型的な正常入力を数個、シードとして置く
  4. AddressSanitizerなどのサニタイザーを有効にして、時間を決めて実行する(Goでは -fuzztime で長さを指定できる)
  5. 見つかった入力を再現テストにし、修正後も回帰テストとして残す
  6. 継続したい場合は、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++については、公式ドキュメントで最新の使い方を確認してください

次に読む

よくある質問

Qファジングとは何ですか?
A

プログラムに、不正な入力や想定外の入力を自動で大量に与え、クラッシュや異常な動作が起きないかを調べるテスト手法です。OWASPは、バグ・脆弱性・想定外の動作を見つけることを目的としたソフトウェアテストの手法と説明しています。ファズテスト(fuzz testing)とも呼びます。

Qファジングでどんな問題が見つかりますか?
A

典型的にはクラッシュ、メモリの破壊(範囲外の読み書きなど)、無限ループ、想定外の例外などです。入力を解析する処理(画像・文書・通信プロトコルのパーサーなど)で特に有効です。ただしOWASPは、ファジングは単純なバグを見つけやすい一方、見つけた問題の影響の大きさを判断しにくいと述べています。

Qカバレッジ誘導型ファジングとは?
A

実行中にどのコードが通ったか(カバレッジ)を計測し、新しいコードに到達した入力を残して、それを少しずつ変えて次の入力を作る方式です。ランダムに入力するより、深い処理まで届きやすくなります。libFuzzerは、組み込み型のカバレッジ誘導・進化型のファジングエンジンと説明されています。

Qファジングを始めるには何が必要ですか?
A

対象の関数に入力を渡す小さなテスト用コード(ファズターゲット)を書きます。C/C++ならlibFuzzerやAFL++、Goなら1.18以降の標準ツール(go test -fuzz)が使えます。メモリの誤用をすぐ検出するため、AddressSanitizerなどのサニタイザーと組み合わせるのが一般的です。

Qファジングは他人のサービスに対して実行してよいですか?
A

いけません。許可のない他人のシステムに大量の不正な入力を送る行為は、不正アクセスや業務妨害にあたるおそれがあります。ファジングは、自分で開発・管理するソフトウェアに対して、自分の環境で行います。オープンソースの場合も、OSS-Fuzzのように提供されている仕組みを通して行うのが安全です。

QOSS-Fuzzとは何ですか?
A

Googleが提供する、オープンソースソフトウェア向けの無料のファジング基盤です。公式ページによると、2016年にOpenSSLのHeartbleed脆弱性を受けて始まり、libFuzzer、AFL++、Honggfuzz、Centipedeなどのエンジンを使い、見つけたバグを開発者に非公開で通知します。