外部結合を使用してデータを関連付ける

以下でサポートされています。

このドキュメントでは、外部結合(左結合と右結合)について説明します。結合オペレーションは、共通のフィールド値に基づいて複数のソースのデータを関連付けて結合するために使用されます。関連するセキュリティ イベントとエンティティを 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 5m
    
    
  • Event-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.hostname
    
    
  • Event-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 5m
    
    
  • Datatable-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.hostname
    
    
  • Datatable-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.hostname
    
    
  • events セクションの明示的な 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 のプロフェッショナルから回答を得ることができます。