外部結合を使用してデータを関連付ける
このドキュメントでは、外部結合(左結合と右結合)について説明します。結合オペレーションは、共通のフィールド値に基づいて複数のソースのデータを関連付けて結合するために使用されます。関連するセキュリティ イベントとエンティティを 1 つの包括的なビューにまとめることで、効果的な脅威の検出と調査を行うことができます。
両方のデータソースに一致するエントリが必要な標準(内部)結合とは異なり、外部結合では、もう一方に一致するエントリがない場合でも、結合の一方の側からすべてのレコードが取得されます。通常、もう一方の側の一致しないフィールドには null が入力されます。これにより、一致しないデータが失われるのを防ぐことができます。
外部結合の仕組み
YARA-L 2.0 の外部結合のコンセプトは、標準 SQL の外部結合と同じです。
左外部結合では、結合の左側のすべてのレコードが保持されます。
右外部結合では、結合の右側のすべてのレコードが保持されます。
外部結合構文(左結合と右結合)は、match 条件の有無にかかわらず、すべてのクエリでサポートされています。外部結合の基本的な動作は、検索、ダッシュボード、検出ルールで同じですが、構文はコンテキストによって異なります。
検索とダッシュボード:
eventsセクションで明示的なleft joinキーワードまたはright joinキーワードを使用します。検出ルール:
eventsセクションで等号(=)を使用して結合関係を定義し、conditionセクションで無制限の条件を指定します(たとえば、左外部結合の場合は#data_table >= 0、アンチ結合除外の場合は#data_table = 0)。
左外部結合について
左外部結合(または左結合)では、検索で left join キーワードを使用するか、検出ルールで無制限の条件(#data_table >= 0)を使用するかに関係なく、結合の左側(保持側)のデータソースのすべてのレコードが保持されます。
左側のレコードが右側のイベントまたはデータテーブルに一致しない場合、右側のフィールドは
nullとして返されます。プレースホルダの含意:
matchセクションで使用されるプレースホルダ変数は、結果セット全体で正確なデータ集計を行うために、左側のイベントのフィールドを参照する必要があります。
イベント間の左結合の例
次の例は、左外部結合を使用して、ユーザー ログイン イベントを同じホストで発生する後続のネットワーク接続イベントに関連付ける方法を示しています。左結合により、すべての USER_LOGIN イベントが結果セットに保持されます。一致する NETWORK_CONNECTION イベント($e2)が見つかった場合、そのデータが結合されます。一致するものが見つからない場合、$e2 のフィールドは null になります。
$e1.metadata.event_type = "USER_LOGIN"
$e2.metadata.event_type = "NETWORK_CONNECTION"
left join $e1.principal.hostname = $e2.principal.hostname
左側のイベントを定義する
次のクエリの例では、結合の左側($e1)を定義しています。これは、最終結果に保持されるイベントセットです。
$e1.metadata.event_type = "USER_LOGIN"
次の表は、クエリ結果を示しています。ユーザー ログイン イベントの初期セットが特定されています。
| イベントタイプ | プリンシパル ホスト名 | IP アドレス |
|---|---|---|
USER_LOGIN
|
workstation-01
|
192.168.1.101
|
USER_LOGIN
|
laptop-hr-02
|
192.168.1.102
|
USER_LOGIN
|
server-db-03
|
10.0.0.50
|
USER_LOGIN
|
kiosk-4
|
192.168.1.104
|
適切なイベントを定義する
次のクエリの例では、結合の右側($e2)を定義します。これは、左側のイベントと照合されるイベントセットです。
$e2.metadata.event_type = "NETWORK_CONNECTION"
次の表に、照合に使用できるネットワーク接続イベントのセットを示します。
| イベントタイプ | プリンシパル ホスト名 | IP アドレス |
|---|---|---|
NETWORK_CONNECTION
|
workstation-01
|
192.168.1.101
|
NETWORK_CONNECTION
|
laptop-hr-02
|
192.168.1.101
|
NETWORK_CONNECTION
|
kiosk-4
|
203.0.113.3
|
イベントに参加する
一致セクション
次の例は、principal.hostname フィールドで左外部結合を使用した一致クエリを示しています。
$e1.metadata.event_type = "USER_LOGIN"
$e2.metadata.event_type = "NETWORK_CONNECTION"
left join $e1.principal.hostname = $e2.principal.hostname
$host = $e1.principal.hostname
match:
$host over 5m
左外部結合により、すべての
USER_LOGINイベント($e1)が最終的な結果セットに含まれます。プレースホルダ $host には、
$e1.principal.hostnameの値が割り当てられます。左外部結合により、イベント$e1が確実に存在し、集計のために $host 変数が常に設定されます。このルールは、5 分間の時間枠でホストごとに結果を集計します。
結合結果
結果のデータには、2 つのイベントの組み合わせが表示されます。左側のテーブル($e1)のすべてのレコードが保持され、一致するホスト名が見つからない場合(server-db-03 など)、右側のテーブル($e2)のフィールドは null に設定されます。
イベントタイプ($e1)
|
プリンシパル ホスト名($host) |
IP アドレス($e1)
|
イベントタイプ($e2)
|
IP アドレス($e2)
|
一致ステータス |
|---|---|---|---|---|---|
USER_LOGIN
|
workstation-01
|
192.168.1.101
|
NETWORK_CONNECTION
|
192.168.1.101
|
一致するドキュメントが見つかりました |
USER_LOGIN
|
laptop-hr-02
|
192.168.1.102
|
NETWORK_CONNECTION
|
192.168.1.101
|
一致するドキュメントが見つかりました |
USER_LOGIN
|
server-db-03
|
10.0.0.50
|
null
|
null
|
一致なし |
USER_LOGIN
|
kiosk-4
|
192.168.1.104
|
NETWORK_CONNECTION
|
203.0.113.3
|
一致するドキュメントが見つかりました |
左結合クエリの例
このセクションでは、左結合クエリの例を示します。
一致条件を使用した結合
イベント エンティティ
$e1.metadata.event_type = "NETWORK_CONNECTION" $g1.graph.metadata.entity_type = "ASSET" left join $e1.principal.asset.hostname = $g1.graph.entity.asset.hostname $host = $e1.principal.asset.hostname match: $host over 5mEvent-datatable
$host = $e1.principal.hostname left join $e1.principal.hostname = %all_dt_column_types.hostname match: $host by 5m
一致条件のない結合
イベント - イベント
$e1.metadata.event_type = "USER_LOGIN" $e1.principal.ip = "114.241.96.87" $e2.metadata.event_type = "NETWORK_CONNECTION" left join $e1.principal.hostname = $e2.principal.hostnameイベント エンティティ
$e1.metadata.event_type = "NETWORK_CONNECTION" $g1.graph.metadata.entity_type = "ASSET" left join $e1.principal.asset.hostname = $g1.graph.entity.asset.hostname $host = $e1.principal.asset.hostnameEvent-datatable
$host = $e1.principal.hostname left join $e1.principal.hostname = %all_dt_column_types.hostname
検出ルールの左結合
検出ルールでは、events セクションで left join キーワードを使用しません。代わりに、等価(=)を使用して events セクションで UDM イベントをデータテーブルまたはセカンダリ イベントにリンクし、condition セクションで結合動作を制御します。
| 結合タイプ | events セクション |
condition セクション |
一致しないイベントに対する動作 |
|---|---|---|---|
| 内部結合 | $key = %data_table.key |
$e and #data_table > 0 |
一致しないイベントは検出ストリームから除外されます。 |
| 左外部結合(拡充) | $key = %data_table.key |
$e and #data_table >= 0 |
$e 内のすべてのイベントが保持されます。一致しないデータテーブル フィールドは null です。 |
| アンチ結合(除外) | $key = %data_table.key |
$e and #data_table = 0 |
イベントキーがデータテーブルに存在しない場合にのみアラートをトリガーします。 |
ルールでのイベントからデータテーブルへの左結合の例
次の例では、corporate_asset_directory という名前のデータテーブルを使用して、管理対象外のホストからのイベントをドロップせずに、アセットの重要度と地理的メタデータでイベントを拡充します。
データテーブルを定義する
corporate_asset_directory データテーブルには、hostname をキー列として使用する次の行が含まれています。
hostname(鍵) |
country_code |
country_name |
asset_tier |
owner_dept |
risk_score |
|---|---|---|---|---|---|
ES01-W10 |
ES |
Spain |
TIER_3_WORKSTATION |
Finance |
20 |
CH02-SRV |
CH |
Switzerland |
TIER_1_CRITICAL |
Engineering |
80 |
US03-LAP |
US |
United States |
TIER_2_PRODUCTION |
Executive |
50 |
内部結合と左外部結合の条件を比較する
内部結合(
#corporate_asset_directory > 0): データテーブル数が0より大きい場合は、内部結合が実行されます。イベントがcorporate_asset_directoryにリストされていない管理対象外のホスト(unmanaged-byod-09など)から発生した場合、イベントはフィルタされ、検出は生成されません。rule inner_join_drops_unmatched_events { meta: author = "Google SecOps" description = "Inner join example: drops events whose hostname is not in the data table." events: $e.principal.hostname != "" $hostname = $e.principal.hostname // Direct inner join against data table $hostname = %corporate_asset_directory.hostname match: $hostname over 1h outcome: $country = array_distinct(%corporate_asset_directory.country_code) $asset_tier = array_distinct(%corporate_asset_directory.asset_tier) $risk_score = max(%corporate_asset_directory.risk_score) condition: $e and #corporate_asset_directory > 0 }左外部結合(
#corporate_asset_directory >= 0): 制限のない条件#corporate_asset_directory >= 0を使用すると、左外部結合が実行されます。ホスト名がcorporate_asset_directoryの行と一致しない場合でも、$eのすべてのイベントが保持されます。イベントがデータテーブルの行と一致しない場合、
%corporate_asset_directory.columnへの参照はnullと評価されます。outcomeセクションで、文字列の列にはstrings.coalesceを使用し、数値の列には条件付きif式を使用して、デフォルトのフォールバック値を割り当てます。rule left_outer_join_enrichment { meta: author = "Google SecOps" description = "Left outer join using #dt >= 0: preserves all events and populates enrichment fields." events: $e.principal.hostname != "" $hostname = $e.principal.hostname // Outer join equality binding $hostname = %corporate_asset_directory.hostname match: $hostname over 1h outcome: $country = array_distinct(strings.coalesce(%corporate_asset_directory.country_code, "UNKNOWN")) $asset_tier = array_distinct(strings.coalesce(%corporate_asset_directory.asset_tier, "UNMANAGED")) $risk_score = max(if(%corporate_asset_directory.risk_score > 0, %corporate_asset_directory.risk_score, 0)) condition: $e and #corporate_asset_directory >= 0 }
ルールの結合結果
次の表は、#corporate_asset_directory >= 0 を使用した場合の一致するホストと一致しないホストの結果値を比較したものです。
プリンシパル ホスト名($hostname) |
データテーブルの一致ステータス | $country |
$asset_tier |
$risk_score |
|---|---|---|---|---|
CH02-SRV |
一致します | ["CH"] |
["TIER_1_CRITICAL"] |
80 |
ES01-W10 |
一致します | ["ES"] |
["TIER_3_WORKSTATION"] |
20 |
unmanaged-byod-09 |
一致なし(null はデフォルトに置き換えられます) |
["UNKNOWN"] |
["UNMANAGED"] |
0 |
ルールでの左アンチ結合(除外)の例
ゼロカウント条件(#data_table = 0)で左外部結合を使用して、左アンチ結合を実行できます。このパターンでは、イベントが除外データテーブルと一致すると検出が抑制され、一致がない場合にのみアラートがトリガーされます。
単一列の除外(
#authorized_scanners_exclusion = 0): 次のルールは、$e.principal.hostnameをauthorized_scanners_exclusionデータテーブルと結合し、ホストがテーブルに存在しない場合にのみトリガーされます。rule anti_join_scanner_exclusion { meta: author = "Google SecOps" description = "Anti-join exclusion using #dt = 0: triggers only if the host is not in the authorized scanners table." events: $e.principal.hostname != "" $hostname = $e.principal.hostname // Bind join key to data table $hostname = %authorized_scanners_exclusion.hostname match: $hostname over 1h outcome: $total_sent_bytes = sum($e.network.sent_bytes) condition: $e and #authorized_scanners_exclusion = 0 }または、
outcomeセクション($scanner_match_count = count(%authorized_scanners_exclusion.hostname))で一致したデータテーブルの行をカウントし、conditionセクションで$e and $scanner_match_count = 0を評価することもできます。rule anti_join_outcome_count { meta: author = "Google SecOps" description = "Anti-join via outcome variable count check: triggers when scanner match count is zero." events: $e.principal.hostname != "" $hostname = $e.principal.hostname $hostname = %authorized_scanners_exclusion.hostname match: $hostname over 1h outcome: $total_sent_bytes = sum($e.network.sent_bytes) $scanner_match_count = count(%authorized_scanners_exclusion.hostname) condition: $e and $scanner_match_count = 0 }複数列の行タプルの除外(
#multi_column_admin_exceptions = 0): 複数のフィールドの特定の組み合わせが例外データテーブルの同じ行と一致する場合にのみイベントを除外するには、各イベント フィールドをeventsセクションの対応する列にバインドし、conditionセクションで#data_table = 0を指定します。rule multi_column_tuple_exclusion { meta: author = "Google SecOps" description = "Multi-column row-tuple anti-join: excludes activity matching composite (origin_ip, target_ip, username, action) rows." events: // Bind event fields to match variables $e.principal.ip = $src_ip $e.target.ip = $dst_ip $e.principal.user.userid = $user $e.metadata.product_event_type = $action // Multi-column equality against the same data table row $src_ip = %multi_column_admin_exceptions.origin_ip $dst_ip = %multi_column_admin_exceptions.target_ip $user = %multi_column_admin_exceptions.username $action = %multi_column_admin_exceptions.action match: $src_ip, $dst_ip, $user, $action over 5m outcome: $event_count = count($e.metadata.id) condition: $e and #multi_column_admin_exceptions = 0 }
右外部結合
右外部結合(または右結合)では、right join キーワードの右側にあるデータソースのすべてのレコードが保持されます。
右側のイベントのレコードが左側のイベントに一致しない場合、左側のイベントのフィールドは
nullとして返されます。プレースホルダの意味:
matchセクションで使用されるプレースホルダ変数は、結果セット全体で正確なデータ集計を行うために、右側のイベントのフィールドを参照する必要があります。
イベント間の右結合の例
次の例は、ユーザー ログイン イベントを、同じホストで発生する後続のネットワーク接続イベントに関連付ける right outer join を示しています。right join を使用すると、すべての NETWORK_CONNECTION イベントが結果セットに保持されます。一致する USER_LOGIN イベントが見つかった場合、そのデータが結合されます。一致するものが見つからない場合、$e1 のフィールドは null になります。
$e1.metadata.event_type = "USER_LOGIN"
$e2.metadata.event_type = "NETWORK_CONNECTION"
right join $e1.principal.hostname = $e2.principal.hostname
左側のイベントを定義する
次のクエリは、結合の左側($e1)を定義します。これは、最終結果のオプションのイベントセットです。
$e1.metadata.event_type = "USER_LOGIN"
次の表は、クエリの結果を示しています。ユーザー ログイン イベントの初期セットが特定されています。
| イベントタイプ | プリンシパル ホスト名 | IP アドレス |
|---|---|---|
USER_LOGIN
|
workstation-01
|
192.168.1.101
|
USER_LOGIN
|
laptop-hr-02
|
192.168.1.102
|
USER_LOGIN
|
server-db-03
|
10.0.0.50
|
適切なイベントを定義する
次のクエリは、結合の右側($e2)を定義します。これは、最終結果に保持されるイベントセットです。
$e2.metadata.event_type = "NETWORK_CONNECTION"
次の表に、照合に使用できるネットワーク接続イベントのセットを示します。
| イベントタイプ | プリンシパル ホスト名 | IP アドレス |
|---|---|---|
NETWORK_CONNECTION
|
workstation-01
|
192.168.1.101
|
NETWORK_CONNECTION
|
laptop-hr-02
|
192.168.1.101
|
NETWORK_CONNECTION
|
vm-unauth-05
|
203.0.113.3
|
イベントに参加する
次の例は、principal.hostname フィールドに右外部結合を使用した一致クエリを示しています。
$e1.metadata.event_type = "USER_LOGIN"
$e2.metadata.event_type = "NETWORK_CONNECTION"
right join $e1.principal.hostname = $e2.principal.hostname
$host = $e1.principal.hostname
match:
$host over 5m
右外部結合により、すべての
NETWORK_CONNECTIONイベント($e2)が最終的な結果セットに含まれます。プレースホルダ
$hostには、$e2.principal.hostnameの値が割り当てられます。右外部結合により、イベント$e2が確実に存在し、集計のために$host変数が常に設定されます。このルールは、5 分間の時間枠でホストごとに結果を集計します。
結合結果
結果のデータセットには、2 つのイベントの組み合わせが表示されます。右側のテーブル($e2)のすべてのレコードが保持され、一致するホスト名が見つからない場合(vm-unauth-05 など)、左側のテーブル($e1)のフィールドは null に設定されます。
イベントタイプ($e1)
|
プリンシパル ホスト名($e1) |
IP アドレス($e1)
|
イベントタイプ($e2)
|
プリンシパル ホスト名($host) | IP アドレス($e2)
|
一致ステータス |
|---|---|---|---|---|---|---|
USER_LOGIN
|
workstation-01
|
192.168.1.101
|
NETWORK_CONNECTION
|
workstation-01
|
192.168.1.101
|
一致するドキュメントが見つかりました |
USER_LOGIN
|
laptop-hr-02
|
192.168.1.102
|
NETWORK_CONNECTION
|
laptop-hr-02
|
192.168.1.101
|
一致するドキュメントが見つかりました |
null
|
null
|
null
|
NETWORK_CONNECTION
|
vm-unauth-05
|
203.0.113.4
|
一致なし |
右結合クエリの例
このセクションでは、右結合クエリの例を示します。
一致条件を使用した結合
イベント - イベント
$e1.metadata.event_type = "USER_LOGIN" $e2.metadata.event_type = "NETWORK_CONNECTION" right join $e1.principal.hostname = $e2.principal.hostname $host = $e2.principal.hostname match: $host over 5mエンティティ イベント
$e1.metadata.event_type = "NETWORK_CONNECTION" $g1.graph.metadata.entity_type = "ASSET" right join $g1.graph.entity.asset.hostname = $e1.principal.asset.hostname $host = $e1.principal.asset.hostname match: $host over 5mDatatable-event
$host = $e1.principal.hostname right join %all_dt_column_types.hostname = $e1.principal.hostname match: $host by 5m
一致条件のない結合
イベント - イベント
$e1.metadata.event_type = "USER_LOGIN" $e1.principal.ip = "114.241.96.87" $e2.metadata.event_type = "NETWORK_CONNECTION" right join $e1.principal.hostname = $e2.principal.hostnameエンティティ イベント
$e1.metadata.event_type = "NETWORK_CONNECTION" $g1.graph.metadata.entity_type = "ASSET" right join $g1.graph.entity.asset.hostname = $e1.principal.asset.hostname $host = $e1.principal.asset.hostnameDatatable-event
$host = $e1.principal.hostname right join %all_dt_column_types.hostname = $e1.principal.hostname
制限事項
外部結合を作成する際は、次の制限事項を考慮してください。
完全外部結合(左結合と右結合の組み合わせ)はサポートされていません。
一致しない結合のクエリ期間は最大 14 日に制限されています。
2 つのコンテキスト ソースを直接結合することはできません(たとえば、エンティティをデータテーブルに直接結合することはできません)。
プライマリ統合データモデル(UDM)イベントは、外部結合の保持側である必要があります。プライマリ イベントが「null 許容」側にある場合、クエリは無効です。
イベント エンティティの結合は左結合にする必要があります。これにより、イベント(
$e1)が正しく保持されます。エンティティ イベント結合は右結合にする必要があります。これにより、イベント(
$e1)が正しく保持されます。
次の例は、UDM イベント(
$e1)が左側にありますが、右結合では右側($g1)が保持されるため、UDM イベントを保持する必要があるというルールに違反しているため、無効です。// Invalid query $e1.metadata.event_type = "NETWORK_CONNECTION" $g1.graph.metadata.entity_type = "ASSET" right join $e1.principal.asset.hostname = $g1.graph.entity.asset.hostnameeventsセクションの明示的なleft joinキーワードとright joinキーワードは、検出ルールではサポートされていません。YARA-L 検索とダッシュボードでのみサポートされています。検出ルールで左外部結合またはアンチ結合を実行するには、conditionセクションで#data_table >= 0や#data_table = 0などの無制限の条件を使用します。詳細については、検出ルールの左結合をご覧ください。
ベスト プラクティス
外部結合クエリのパフォーマンス低下とクエリ タイムアウトを防ぐには、具体的で狭いフィルタを使用します。
たとえば、次のような広範なクエリがあるとします。
$e1.metadata.event_type = "USER_LOGIN"
$e2.metadata.event_type = "NETWORK_CONNECTION"
right join $e1.principal.hostname = $e2.principal.hostname
次の特定の条件を追加することで最適化できます。
$e1.metadata.event_type = "USER_LOGIN"
$e1.principal.ip = "121.121.121.121"
$e1.principal.user.userid = "alex"
$e2.metadata.event_type = "NETWORK_CONNECTION"
$e2.src.hostname = "altostrat.com"
$e1.principal.hostname = $e2.principal.hostname
さらにサポートが必要な場合 コミュニティ メンバーや Google SecOps のプロフェッショナルから回答を得ることができます。