ガードギャップの監査:保護されていないピアがバグである
ほとんどのアクセス制御やバリデーション欠落のバグは特殊なものではなく、同じファミリー内のひとつのハンドラが、その兄弟が実行しているチェックを忘れているというものです。admin_delete はロールをチェックするが、bulk_delete はチェックしない。read_bounded は長さを検証するが、read_fast はそれを信頼する。ファイルを上から下へ読むだけではこれらは見えません。ピアを比較することで浮かび上がります。これは最も収穫量の高いホワイトボックス手法のひとつです。
あなたが探す非対称性
ガード → シンク (意図された、安全なパス)
??? → シンク (ガードが欠落したピアパス - バグ)
使用するタイミング
- あるシンクが複数の呼び出しサイトから到達可能で、そのうちのひとつがチェックをスキップしていると疑われる場合。
- 認可モデル、パーサーファミリー、ハンドラグループのチェック忘れを監査している場合。
- ある発見事項について、「なぜこれが間違っているのか」を正しいピアとの具体的な差分として説明する必要がある場合。
スコープチェック
許可されたソースのみ。認可を特定できない場合は、中止してください。
ループ
-
ガードされたアンカーを選ぶ。 敏感なシンクの前に検証を行っている関数を見つけてください - 認可/所有権チェック、境界チェック、許可リスト、サニタイザなど。これが「ここでの正しい状態」のリファレンスとなります。
-
ガードをそれが保護するシンクに固定する。 正確なチェックと正確なシンクを特定します。「正しい」とは、ガードがシンクを支配していることを意味します。つまり、アンカーを通るすべての経路で、シンクの前にチェックが実行されます。関数内に単に存在しているだけでは支配とは言えません。
-
構造的なピアを列挙する。 同じファイルの隣接関数に限定せず、構造的な類似性に注目します:同じシンクに到達する関数、同じインターフェース/仮想メソッドのオーバーライド、同じルートテーブル/ディスパッチマップ/コマンドスイッチ内のハンドラ、クラスの並列メンバー(
get_x/set_x/delete_x)、共有ヘルパーの呼び出し元で一部は引数をサニタイズし一部は生で渡すもの。同じシンクに到達するピアが最も強いシグナルです。 -
各ピアをアンカーと比較する - ガードの名前ではなく、ガードが強制する内容で。 すべてのピアについて、同等のチェックが同じシンクを支配しているかどうかを問い合わせます。ギャップを分類します:
- 欠落 - 同等のチェックがまったくない。
- 迂回可能 - 存在するが、一部の経路でスキップ可能(早期リターン、短絡
||、チェックをジャンプする分岐)。 - ピアより弱い - 異なる種類のチェック:アンカーが認可を行うところで認証を行う、アンカーが境界チェックを行うところで型チェックを行う、アンカーが兄弟フィールドもチェックするのにひとつのフィールドだけチェックする。
- シンク後 - チェックは実行されるが、危険な操作の後に行われる。
- コンテキスト誤り - このシンクのペイロードクラスをカバーしない実際のチェック(シンクの言語に対して間違ったエンコーダ)。
-
攻撃者入力による到達可能性を確認する。 ガードされていないシンクは、信頼できない入力が到達する場合にのみバグです。ピアの危険な引数を実際のソースまでトレースしてください(
adjudicating-taint-pathsに委ねる)。定数でのみ呼び出されるピアは発見事項ではありません。 -
スキーマに従って差分を記録する。 次のように記述します:「アンカーAはシンクSをチェックCでガードしている(支配的)。ピアBはシンクSに
guard_status= <欠落 / 迂回可能 / ピアより弱い / シンク後 / コンテキスト誤り> で到達し、Bの引数は <ソース> を介して攻撃者制御下にある。」発見スキーマ に従って出力します。
実例
アンカー:
delete_own_post(user, post_id)-post.owner == user.idをチェックし(支配的)、その後にdb.delete(post)を実行。 ピア:admin_bulk_delete(ids)は同じdb.deleteシンクに到達。唯一のゲートはルートレベルの@login_requiredで、オブジェクト単位の所有権チェックはなし。 差分:guard_status = ピアより弱い- 認証は存在するが、認可(所有権)は欠落。 到達可能性:idsはリクエストボディから直接来ており、認証されたユーザーなら誰でもアクセス可能。IDOR/BFLA確認、深刻度 高 - 「ログイン済みユーザーは誰でも、任意のユーザーの投稿をIDで削除できる」
拒否すべき合理化
- 「関数内にチェックがあるので、ガードされている。」 → それがシンクを支配している場合のみ。シンクがスキップする分岐上のチェックはガードではありません。
- 「両方の関数に認証があるので、同等だ。」 → 認証は認可ではありません。型チェックは境界チェックではありません。強制される内容を比較してください。
- 「ピアがガードされていないように見える - 報告しよう。」 → 攻撃者入力がシンクに到達することを確認してからにしてください。到達不能 ≠ 発見事項。
- 「同じファイルの隣接関数のみ。」 → 危険なピアは、同じシンクに到達する別のモジュールに存在することがよくあります。ファイルではなく構造で列挙してください。
実践的な実行
関数を到達するシンクでグループ化し、それぞれの正確な本体を表示できるツールが必要です。これにより、ガードをガードごとに比較し、支配性を確認できます。コードプロパティグラフを使用すると、「どのピアがこのシンクに到達し、どれが最初に検証するか」を直接的に答えられます。それ以外の場合は、共有シンクまたはヘルパーの呼び出しサイトからピアを手動で列挙し、それぞれを読み取ります。到達可能性のステップでは、adjudicating-taint-paths を再利用します。
関連
adjudicating-taint-paths- ピアのシンクが入力で到達可能であることを確認します。hunting-bugs-with-a-code-graph- このスキルが提供するマスターループ。- FINDING-SCHEMA.md - すべての発見事項がとる形式。