ロケーションドリフトマッチング (Pro) (Pro)
ロケーションドリフトマッチングを使うと、再インポート時にロケーションが変化した検出事項を同一の検出事項として認識できます。この機能がない場合、再インポートはロケーションフィールドを含む完全一致のアイデンティティハッシュで検出事項を照合するため、ロケーションが変化するたびに古い検出事項がクローズされ、同一内容の新しい検出事項が作成されてしまいます。
- コミットによってコードがシフトし、検出事項の行番号が変わる。
- リファクタリングによってファイルがリネームまたは移動される。
- DASTスキャンの間にWebアプリケーションのURL、ポート、またはホストが変わる。
- 依存関係のバージョンアップにより、SCAツールが報告する脆弱なパッケージのバージョンが変わる。
これらはいずれも従来、クローズされた検出事項と「新規」検出事項の両方を生み出していました。元の検出事項が持っていたステータス、メモ、SLAクロック、リスク受容、JIRA連携が失われ、誤った「新規の重大な検出事項」というノイズが発生していたのです。ロケーションドリフトマッチングを有効にすると、検出事項は1件のまま維持されます。ロケーションは最新のスキャンで更新され、履歴は保持されます。
ロケーションドリフトマッチングはDefectDojo Proの機能です。デフォルトでは無効になっており、セキュリティツールごとに有効化します。
ロケーション追跡の有効化
ロケーション追跡はツールごとに以下の場所で設定します。 Settings > Finding Workflow > Reimport Deduplication(以前のメニュー構成を使用しているインスタンスではSettings > Pro Settings > Deduplication Settings > Reimport Deduplication)
- Security Toolを選択します。
- Deduplication AlgorithmをHash Codeに設定します。ロケーション追跡はHash Codeアルゴリズムにのみ適用されます。信頼できるUnique ID From Toolを持つツールは、その安定したIDを通じて既に移動を追跡できているため、この機能は不要です。
- Track findings as locations changeを有効にします。
設定を保存すると、そのツールの既存の検出事項に対してバックグラウンドでの再ハッシュ処理が自動的にトリガーされます(下記の既存データへの適用を参照)。そのため、トグルを有効にする前にインポートされた検出事項もすぐに対象となります。
マッチングの仕組み
追跡を有効にすると、再インポートのマッチングは2段階で行われます。
- 安定したアイデンティティ。 再インポートハッシュは、変動しやすいロケーションフィールド(行、ファイルパス、説明、コンポーネント名/バージョン、エンドポイント)を除いて計算されます。そのため、検出事項のアイデンティティは、それが現在どこにあるかではなく、それが何であるかを捉えます。移動していない検出事項は、最初にそのまま完全一致し、決して影響を受けません。
- 証拠に基づくペアリング。 安定したアイデンティティを共有する検出事項のグループごとに、ロケーションマッチャーがロケーションの証拠を用いて、受信した検出事項と既存の検出事項をペアリングします。これは、強いものから弱いものへと決定論的なパスで行われます。検出事項は、保持しているロケーションデータに基づいて、いずれか1つのマッチャーにのみ振り分けられます。
コードの検出事項(SAST)
| パス | ペアリングする条件 | 備考 |
|---|---|---|
| 完全一致 | 同じファイルと行 | 常に優先される。移動した近傍の検出事項が、移動していない検出事項のマッチを「奪う」ことは決してない |
| データフロー | 同じソース/シンクオブジェクト(sast_source_object / sast_sink_object) | データフローを報告するツール向け。行番号の振り直しの影響を受けない |
| 最近傍行 | 同じファイル、最も近い行番号 | 貪欲法で近い順に処理。同一ファイル内のみ |
| ファイルリネーム | 異なるファイル | 受信側・既存側それぞれちょうど1件ずつ残っている場合のみ。曖昧な場合はフェイルクローズ(マッチさせない) |
URLの検出事項(DAST)
| パス | ペアリングする条件 |
|---|---|
| 完全一致 | エンドポイントの集合が完全に同一 |
| エンドポイント集合のドリフト | エンドポイントの集合が一部重複(エンドポイントの追加・削除) |
| ポート移動 | 同じホストとパスで、ポートが異なる |
| パスのドリフト | 同じホストで、類似したパス(相互最良のセグメント類似度による) |
| ホスト移動 | 異なるホスト。ワイルドカードDNSに対するガードを備えた、曖昧さのない1対1のペアリングとしてのみ |
依存関係の検出事項(SCA)
| パス | ペアリングする条件 |
|---|---|
| 完全一致 | 同じパッケージ、バージョン、マニフェスト |
| バージョンアップ | 同じパッケージ、異なるバージョン |
| マニフェスト移動 | 同じパッケージ、異なるロックファイル/マニフェストパス |
同じ脆弱なパッケージが複数のマニフェストに存在する場合、各マニフェストの検出事項はそれぞれ独立して追跡されます。あるロックファイルでのバージョンアップが、別のロックファイルの検出事項を飲み込んでしまうことはありません。
深刻度の再スコアリング
セキュリティツールは、ルールエンジンの進化に伴って深刻度を再スコアリングすることがあります。追跡を有効にすると、ツールが報告する深刻度の変更によって検出事項のアイデンティティが分裂することはありません。検出事項はマッチし、その深刻度はスキャン結果から更新されます。ただし、人が手動で深刻度を再トリアージしていた場合は、常に人による値が優先されます(下記参照)。
保持される項目、更新される項目
ドリフトマッチングされた検出事項は、そのライフサイクルにおいて重要な情報をすべて保持します。ステータス、メモ、リスク受容、SLA日付、JIRA連携、検出事項IDです。
そのロケーションフィールド(ファイルパス、行、データフローフィールド、エンドポイント、コンポーネントバージョン)は、受信したスキャンから更新されます。
その説明的フィールド(タイトル、説明、深刻度、コンポーネントバージョン)は、スキャンが依然としてそのフィールドを所有している場合に限り、スキャンから更新されます。DefectDojoは、インポート/再インポートによって最後に書き込まれた各フィールドのダイジェストを記録しています。現在の値がそのダイジェストと一致していれば、それはツールが書き込んだものであり、スキャンによる更新が許可されます。それ以降に人がそのフィールドを編集していた場合は、人による値が永続的に保持されます。この機能より前に作成された検出事項にはダイジェストが存在しないため、人が所有しているものとして扱われ、再インポートによってその説明的フィールドが上書きされることはありません。唯一の例外はコンポーネントバージョンです。これは人がほぼ手動で編集することのないスキャンのテレメトリデータであるため、ダイジェストがなくても更新されます。これにより、移行されたSCAの検出事項でもバージョン更新が反映されます。
アイデンティティは常にツールの報告内容に追従する
マッチした検出事項が更新される際、保存されているアイデンティティハッシュは受信したスキャンの値から採用され、検出事項の現在のフィールドから再計算されることは決してありません。この違いは重要です。更新後の検出事項のフィールドは、スキャンの値と人による編集が混在したものであり、その混在した値から計算したハッシュには、今後どのスキャンも二度と報告しないような値が含まれてしまい、その検出事項に対する以後のすべての再インポートを静かに壊してしまうことになります。値を採用する方式であれば、人が検出事項の名前を変更したり、深刻度を再トリアージしたり、説明を編集したりしても、次のスキャンとマッチする能力が損なわれることは決してありません。
ロケーション履歴
Locations(ベータ版)を有効にすると、すべてのドリフトマッチについて、検出事項が以前どこにあったかが記録されます。置き換えられたソースコードのロケーション、URL、または依存関係のバージョンは、どこにいつ移動したか、なぜ移動したかとともに、参照情報として検出事項に保持されます。検出事項のロケーションのタイムライン、すなわち「この検出事項はauth.py:42にあり、その後auth.py:57、さらにsession.py:31へと移動した」といった履歴は、検出事項のページで確認できます。ソースコードロケーションを参照してください。
ロケーションドリフトマッチング自体は、Locations機能の有無にかかわらず動作します。マッチングは検出事項自身のフィールドとエンドポイントに基づいてペアリングを行うため、いずれの場合でも検出事項は移動を乗り越えて存続します。Locationsは、その上に記録され可視化された履歴を追加するものです。履歴の記録は、Locationsが有効化された時点から開始されます。それ以前の移動は適用されてはいますが、記録はされていません。
既存データへの適用(アップグレード)
この機能は、自己移行するように設計されています。
- オプトインするまで何も変わりません。 トグルがオフの間は、再インポートハッシュは従来通りに計算されます。
- トグルを保存すると既存の検出事項が再ハッシュされます。 バックグラウンドジョブが、そのツールに保存されている再インポートハッシュを新しい(ロケーションを含まない)アイデンティティで再計算し、オープンソースから移行されたデータについて不足しているPro検出事項レコードを作成します。完了すると、古い検出事項と新しい検出事項は同じアイデンティティの言語で扱われるようになります。数か月前にインポートされた検出事項も、昨日インポートされた検出事項とまったく同じように追跡されます。
- 大規模なインスタンスではスキャンの実行の合間に有効化してください。 再ハッシュ処理は、そのツールの検出事項全体に対するバックグラウンドジョブです。処理が進行中に再インポートが到着すると、新旧のハッシュが混在した状態になり、未処理の部分が1回だけ変動することがあります。トグルは静かな時間帯に切り替え、次にスケジュールされている再インポートの前にジョブを完了させてください。
- 手動で編集されたタイトル。 オプトイン時の再ハッシュは、データベースの現在の値から計算されます。よく編集されるフィールドはいずれも追跡対象のアイデンティティから除外されており、深刻度の編集は再ハッシュによってむしろ修復されます。しかし、人が検出事項のタイトルを変更しており(かつそのツールにおいてタイトルがハッシュフィールドである)場合は、その検出事項だけは次回の再インポート時に1回だけ変動してから安定します。
追跡対象ツールのハッシュフィールドの選び方
ロケーション追跡は、変動しやすいロケーションフィールドを再インポートハッシュから自動的に除外します。そのため、ツールのハッシュ設定からlineやfile_pathを自分で削除する必要はありません。ただし、次の2つの設定には注意が必要です。
- すべてが変動フィールドである設定。 ツールのハッシュフィールドがすべてロケーションフィールドである場合(例えば
file_pathとlineのみ)、それらを除外すると何も残らず、ハッシュは従来のタイトル+CWEのアイデンティティにフォールバックします。マッチング自体は機能します(証拠に基づくパスが識別力を担います)が、アイデンティティの粒度はかなり粗くなります。少なくとも1つの安定したコンテンツフィールドを残す設定を推奨します。 - 安定したフィールドの中にロケーション情報が埋め込まれている場合。 ロケーションデータが、ハッシュに残さざるを得ないフィールドの内部に隠れている場合、フィールドの除外では対処できません。「SQL Injection in queries.py:42」のようにタイトルを付けるツールは、行が移動するたびにタイトルが変わってしまい、アイデンティティが分裂してペアを認識できなくなります。このようなツールでは、情報が漏れ出しているフィールドを避けたハッシュフィールドを選んでください。CWE + Content Fingerprintが有力な組み合わせです(Content Fingerprintを参照)。
重複排除との関係
ロケーション追跡は再インポートの機能です。Same ToolおよびCross Toolの重複排除は変更されません。そのハッシュは従来通りに計算され、フィールドの除外が適用されることもありません。ただし、意図的に統合されている点が2つあります。
- バージョンアップが依存関係の重複排除を妨げなくなります。 重複排除のロケーションゲートは、通常、2つのSCAの検出事項が同一のパッケージバージョンを参照していることを要求します。追跡が有効なツールでは、パッケージのアイデンティティを共有していれば十分です(エコシステム+パッケージ名。双方が名前空間を持つ場合はそれも比較されます)。これは、再インポートがバージョンアップを同一の検出事項として扱うことと整合的です。これはLocationsが有効な場合のSame Tool重複排除にのみ適用されます。
- クリーンなアイデンティティ入力。 マッチした検出事項はスキャンが報告したハッシュを採用するため、重複排除が利用する値は常にツールが最後に報告した内容を反映します。人による編集がそれらを汚染することはもうありません。
過去の変動履歴の統合
追跡機能を使わずに何年も運用してきたインスタンスでは、「クローズして再作成する」連鎖が蓄積しています。同じ検出事項が、移動するたびにクローズされ、新しいレコードとして再びオープンされてきたのです。管理コマンドはこうした連鎖を検出し(同じマッチャーによってホップごとに連結され、実際に並存していた検出事項が誤ってマージされないよう生存期間の重複チェックも備えています)、各連鎖を最新の検出事項に統合し、古いコピーを生存した検出事項の重複としてマークします。
# Dry run — reports what would be consolidated, changes nothing
./manage.py consolidate_location_churn --product <id>
# Apply, with a confirmation prompt
./manage.py consolidate_location_churn --product <id> --applyこのコマンドはデフォルトでドライランであり、自動的に実行されることはなく、--testまたは--productで対象を絞り込めます。Locationsが有効な場合、生存した検出事項のロケーション履歴は連鎖から再構築されます。
安全対策と制限
- 完全一致が常に優先されます。 移動していない検出事項は、あいまいなパスが実行される前に完全一致でペアリングされます。移動した検出事項がそのマッチを奪うことは決してありません。
- 曖昧な場合はフェイルクローズします。 ファイルリネームとホスト移動は、双方にちょうど1件ずつ候補が残っている場合にのみペアリングされます。2件の検出事項が消えて2件の新しい検出事項が現れたような場合、推測でマッチさせるのではなく、未マッチのままにされます。
- 非常に大きなグループは緩やかに性能が低下します。 単一のアイデンティティバケットがペアリングの上限(40,000回の比較)を超えた場合、そのバケットに対するマッチングは、無制限に時間を消費する代わりに完全一致のみに縮退します。
- 許容されているトレードオフ: 1対1のリネーム/ホスト移動のパスは、ある検出事項が消え、同じ再インポート内で同一の安定したアイデンティティを持つ無関係の検出事項が現れた場合に、誤った継続性を生み出すことがあります。これはリネームを追跡するために意図的に払っている代償であり、安定したアイデンティティ(同じツール、タイトル、CWE、深刻度など)が、ペアリングがどれだけ誤りうるかの範囲を制限しています。
トグルなしでのロケーション更新
ロケーション追跡とは独立して、再インポートはすべてのアルゴリズムにおいて、マッチした検出事項のロケーションを最新の状態に保ちます。Unique ID From Tool(またはその他のアルゴリズム)でマッチした検出事項は、受信したレポートからline、file_path、データフローフィールド、component_versionが更新され、報告されたエンドポイントは追加され、消滅したエンドポイントは緩和済みとしてマークされます。スキャンが省略した値が既存のデータを上書きすることはなく、人が固定したコンポーネントバージョンは保持されます。これにより、uidでマッチしたSASTの検出事項が初回インポート時の行番号を永久に表示し続けるという長年の問題が解消されます。この機能はインスタンス全体でDD_REIMPORT_REFRESH_LOCATION_FIELDS=Falseにより無効化できます。