Mettre en corrélation des données à l'aide de jointures externes
Ce document décrit les jointures externes (jointure gauche et jointure droite). Les opérations de jointure permettent de mettre en corrélation et de combiner des données provenant de plusieurs sources en fonction de valeurs de champ communes. En combinant les événements liés à la sécurité et les entités associées dans une vue unique et complète, vous pouvez détecter les menaces et enquêter efficacement.
Contrairement aux jointures standards (internes), qui nécessitent des entrées correspondantes dans les deux sources de données, une jointure externe récupère tous les enregistrements d'un côté de la jointure, même s'il n'y a pas d'entrées correspondantes de l'autre côté. Les champs non mis en correspondance de l'autre côté sont généralement remplis avec null. Cela vous évite de perdre des données qui n'ont pas de correspondance.
Fonctionnement des jointures externes
Le concept de jointures externes dans YARA-L 2.0 est identique à celui des jointures externes SQL standard :
La jointure externe gauche conserve tous les enregistrements du côté gauche de la jointure.
La jointure externe droite conserve tous les enregistrements du côté droit de la jointure.
La syntaxe de jointure externe (jointure gauche et jointure droite) est compatible avec toutes les requêtes, avec et sans condition match. Le comportement fondamental d'une jointure externe est le même pour la recherche, les tableaux de bord et les règles de détection, bien que la syntaxe diffère selon le contexte :
Recherche et tableaux de bord : utilisez les mots clés explicites
left joinouright joindans la sectionevents.Règles de détection : définissez la relation de jointure à l'aide d'un signe égal (
=) dans la sectioneventset spécifiez une condition non limitée dans la sectioncondition(par exemple,#data_table >= 0pour une jointure externe gauche ou#data_table = 0pour une exclusion d'anti-jointure).
Comprendre la jointure externe gauche
Une jointure externe gauche (ou jointure gauche) conserve tous les enregistrements de la source de données du côté gauche (conservé) de la jointure, que vous utilisiez le mot clé left join dans la recherche ou une condition non limitée (#data_table >= 0) dans une règle de détection.
Si un enregistrement du côté gauche n'a pas de correspondance dans la table d'événements ou de données de droite, les champs du côté droit sont renvoyés en tant que
null.Implication de l'espace réservé : toute variable d'espace réservé utilisée dans la section
matchdoit référencer un champ de l'événement de gauche pour garantir une agrégation précise des données sur l'ensemble des résultats.
Exemple de jointure gauche d'événement à événement
L'exemple suivant illustre une jointure externe gauche pour mettre en corrélation les événements de connexion utilisateur avec les événements de connexion réseau ultérieurs se produisant sur le même hôte.
La jointure gauche garantit que tous les événements USER_LOGIN sont conservés dans l'ensemble de résultats. Si un événement NETWORK_CONNECTION correspondant ($e2) est trouvé, ses données sont associées.
Si aucune correspondance n'est trouvée, les champs de $e2 sont null.
$e1.metadata.event_type = "USER_LOGIN"
$e2.metadata.event_type = "NETWORK_CONNECTION"
left join $e1.principal.hostname = $e2.principal.hostname
Définir l'événement de départ
L'exemple de requête suivant définit le côté gauche de la jointure ($e1), qui correspond à l'ensemble d'événements conservé dans le résultat final :
$e1.metadata.event_type = "USER_LOGIN"
Le tableau suivant représente le résultat de la requête, en identifiant l'ensemble initial d'événements de connexion utilisateur :
| Type d'événement | Nom d'hôte principal | Adresse 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
|
Définir l'événement approprié
L'exemple de requête suivant définit le côté droit de la jointure ($e2), qui est l'ensemble d'événements mis en correspondance avec les événements de gauche :
$e2.metadata.event_type = "NETWORK_CONNECTION"
Le tableau suivant représente l'ensemble des événements de connexion réseau disponibles pour la mise en correspondance :
| Type d'événement | Nom d'hôte principal | Adresse 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
|
Participez aux événements
avec une section de correspondance
L'exemple suivant illustre une requête de correspondance utilisant une jointure externe gauche sur le champ 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
La jointure externe gauche garantit que chaque événement
USER_LOGIN($e1) est inclus dans l'ensemble de résultats final.La valeur de l'espace réservé $host est attribuée à partir de
$e1.principal.hostname. La jointure externe gauche garantit la présence de l'événement$e1, ce qui permet de s'assurer que la variable $host est toujours renseignée pour l'agrégation.La règle regroupe les résultats par hôte pour une période de cinq minutes.
Résultat de la jointure
Les données obtenues montrent la combinaison des deux événements. Tous les enregistrements de la table de gauche ($e1) sont conservés, et les champs de la table de droite ($e2) sont définis sur null lorsqu'aucun nom d'hôte correspondant n'est trouvé (par exemple, pour server-db-03).
Type d'événement ($e1)
|
Nom d'hôte principal ($host)
|
Adresse IP ($e1)
|
Type d'événement ($e2)
|
Adresse IP ($e2)
|
État de la mise en correspondance |
|---|---|---|---|---|---|
USER_LOGIN
|
workstation-01
|
192.168.1.101
|
NETWORK_CONNECTION
|
192.168.1.101
|
Correspondance trouvée |
USER_LOGIN
|
laptop-hr-02
|
192.168.1.102
|
NETWORK_CONNECTION
|
192.168.1.101
|
Correspondance trouvée |
USER_LOGIN
|
server-db-03
|
10.0.0.50
|
null
|
null
|
Aucune correspondance |
USER_LOGIN
|
kiosk-4
|
192.168.1.104
|
NETWORK_CONNECTION
|
203.0.113.3
|
Correspondance trouvée |
Exemples de requêtes de jointure gauche
Cette section fournit des exemples de requêtes de jointure gauche.
Jointures avec une condition de correspondance
Entité d'événement
$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
Jointures sans condition de correspondance
Événement-événement
$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.hostnameEntité d'événement
$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
Jointures gauches dans les règles de détection
Dans les règles de détection, vous n'utilisez pas le mot clé left join dans la section events. À la place, vous associez un événement UDM à une table de données ou à un événement secondaire dans la section events à l'aide de l'égalité (=), et vous contrôlez le comportement de jointure dans la section condition :
| Type de jointure | Section events |
Section condition |
Comportement en cas d'événements sans correspondance |
|---|---|---|---|
| Jointure interne | $key = %data_table.key |
$e and #data_table > 0 |
Les événements sans correspondance sont supprimés du flux de détection. |
| Jointure externe gauche (enrichissement) | $key = %data_table.key |
$e and #data_table >= 0 |
Tous les événements de $e sont conservés. Les champs de table de données sans correspondance sont null. |
| Anti-jointure (exclusion) | $key = %data_table.key |
$e and #data_table = 0 |
Déclenche une alerte uniquement lorsque la clé d'événement n'existe pas dans la table de données. |
Exemple de jointure gauche d'un événement à une table de données dans une règle
L'exemple suivant utilise une table de données nommée corporate_asset_directory pour enrichir les événements avec la criticité des éléments et les métadonnées géographiques sans supprimer les événements des hôtes non gérés.
Définir la table de données
La table de données corporate_asset_directory contient les lignes suivantes, en utilisant hostname comme colonne de clé :
hostname (clé) |
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 |
Comparer les conditions de jointure interne et de jointure externe gauche
Jointure interne (
#corporate_asset_directory > 0) : si le nombre de tables de données est supérieur à0, une jointure interne est effectuée. Si un événement provient d'un hôte non géré qui ne figure pas danscorporate_asset_directory(par exemple,unmanaged-byod-09), l'événement est filtré et aucune détection n'est générée :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 }Jointure externe gauche (
#corporate_asset_directory >= 0) : l'utilisation de la condition non limitée#corporate_asset_directory >= 0effectue une jointure externe gauche. Tous les événements de$esont conservés, même lorsque le nom d'hôte ne correspond pas à une ligne decorporate_asset_directory.Lorsqu'un événement ne correspond pas à une ligne de table de données, les références à
%corporate_asset_directory.columnsont évaluées ànull. Dans la sectionoutcome, utilisezstrings.coalescepour les colonnes de chaîne et les expressionsifconditionnelles pour les colonnes numériques afin d'attribuer des valeurs de remplacement par défaut :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 }
Résultat de la jointure de règles
Le tableau suivant compare les valeurs de résultat obtenues pour les hôtes correspondants et non correspondants lors de l'utilisation de #corporate_asset_directory >= 0 :
Nom d'hôte principal ($hostname) |
État de la correspondance du tableau de données | $country |
$asset_tier |
$risk_score |
|---|---|---|---|---|
CH02-SRV |
Correspondance trouvée | ["CH"] |
["TIER_1_CRITICAL"] |
80 |
ES01-W10 |
Correspondance trouvée | ["ES"] |
["TIER_3_WORKSTATION"] |
20 |
unmanaged-byod-09 |
Aucune correspondance (null remplacé par les valeurs par défaut) |
["UNKNOWN"] |
["UNMANAGED"] |
0 |
Exemples d'anti-jointure gauche (exclusion) dans les règles
Vous pouvez utiliser une jointure externe gauche avec une condition de décompte nul (#data_table = 0) pour effectuer une antijointure gauche. Ce modèle supprime les détections lorsqu'un événement correspond à une table de données d'exclusion et ne déclenche une alerte que lorsqu'aucune correspondance n'existe.
Exclusion d'une seule colonne (
#authorized_scanners_exclusion = 0) : la règle suivante joint$e.principal.hostnameà une table de donnéesauthorized_scanners_exclusionet ne se déclenche que lorsque l'hôte n'est pas présent dans la table :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 }Vous pouvez également compter les lignes de la table de données correspondantes dans la section
outcome($scanner_match_count = count(%authorized_scanners_exclusion.hostname)) et évaluer$e and $scanner_match_count = 0dans la sectioncondition: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 }Exclusion de tuple de ligne multicolonne (
#multi_column_admin_exceptions = 0) : pour exclure des événements uniquement lorsqu'une combinaison spécifique de plusieurs champs correspond à la même ligne d'une table de données d'exception, liez chaque champ d'événement à sa colonne correspondante dans la sectioneventset spécifiez#data_table = 0dans la sectioncondition: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 }
Jointure externe droite
Une jointure externe droite (ou jointure droite) conserve tous les enregistrements de la source de données située à droite du mot clé right join.
Si un enregistrement de l'événement de droite ne correspond à aucun enregistrement de l'événement de gauche, les champs de l'événement de gauche sont renvoyés sous la forme
null.Implication de l'espace réservé : toute variable d'espace réservé utilisée dans la section
matchdoit faire référence à un champ de l'événement de droite pour garantir une agrégation précise des données dans l'ensemble des résultats.
Exemple de jointure droite d'événement à événement
L'exemple suivant illustre un right outer join permettant de mettre en corrélation les événements de connexion des utilisateurs avec les événements de connexion réseau ultérieurs se produisant sur le même hôte.
right join garantit que tous les événements NETWORK_CONNECTION sont conservés dans l'ensemble de résultats. Si un événement USER_LOGIN correspondant est trouvé, ses données sont jointes.
Si aucune correspondance n'est trouvée, les champs de $e1 sont null.
$e1.metadata.event_type = "USER_LOGIN"
$e2.metadata.event_type = "NETWORK_CONNECTION"
right join $e1.principal.hostname = $e2.principal.hostname
Définir l'événement de gauche
La requête suivante définit le côté gauche de la jointure ($e1), qui est l'ensemble d'événements facultatif dans le résultat final :
$e1.metadata.event_type = "USER_LOGIN"
Le tableau suivant représente le résultat de la requête, en identifiant l'ensemble initial d'événements de connexion utilisateur :
| Type d'événement | Nom d'hôte principal | Adresse 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
|
Définir l'événement approprié
La requête suivante définit le côté droit de la jointure ($e2), qui correspond à l'ensemble d'événements conservé dans le résultat final.
$e2.metadata.event_type = "NETWORK_CONNECTION"
Le tableau suivant représente l'ensemble des événements de connexion réseau disponibles pour la mise en correspondance.
| Type d'événement | Nom d'hôte principal | Adresse 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
|
Participez aux événements
L'exemple suivant montre une requête de correspondance avec une jointure externe droite sur le champ 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
La jointure externe droite garantit que chaque événement
NETWORK_CONNECTION($e2) est inclus dans l'ensemble de résultats final.La valeur de
$e2.principal.hostnameest attribuée à l'espace réservé$host. La jointure externe droite garantit la présence de l'événement$e2, ce qui permet de s'assurer que la variable$hostest toujours renseignée pour l'agrégation.La règle regroupe les résultats par hôte pour une période de cinq minutes.
Résultat de la jointure
L'ensemble de données obtenu affiche la combinaison des deux événements. Tous les enregistrements de la table de droite ($e2) sont conservés, et les champs de la table de gauche ($e1) sont définis sur "null" lorsqu'aucun nom d'hôte correspondant n'est trouvé (par exemple, vm-unauth-05).
Type d'événement ($e1)
|
Nom d'hôte principal ($e1)
|
Adresse IP ($e1)
|
Type d'événement ($e2)
|
Nom d'hôte principal ($host) | Adresse IP ($e2)
|
État de la mise en correspondance |
|---|---|---|---|---|---|---|
USER_LOGIN
|
workstation-01
|
192.168.1.101
|
NETWORK_CONNECTION
|
workstation-01
|
192.168.1.101
|
Correspondance trouvée |
USER_LOGIN
|
laptop-hr-02
|
192.168.1.102
|
NETWORK_CONNECTION
|
laptop-hr-02
|
192.168.1.101
|
Correspondance trouvée |
null
|
null
|
null
|
NETWORK_CONNECTION
|
vm-unauth-05
|
203.0.113.4
|
Aucune correspondance |
Exemples de requêtes de jointure droite
Cette section fournit des exemples de requêtes de jointure droite.
Jointures avec une condition de correspondance
Événement-événement
$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 5mEntité-événement
$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
Jointures sans condition de correspondance
Événement-événement
$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.hostnameEntité-événement
$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
Limites
Tenez compte des limites suivantes lorsque vous créez des jointures externes :
Une jointure externe complète (jointure gauche et jointure droite) n'est pas acceptée.
La période de la requête pour les jointures sans correspondance est limitée à 14 jours maximum.
Vous ne pouvez pas joindre directement deux sources contextuelles (par exemple, une entité directement à une table de données).
L'événement principal du modèle de données unifié (UDM) doit être le côté conservé de la jointure externe. La requête n'est pas valide si l'événement principal se trouve du côté "nullable".
Une jointure événement-entité doit être une jointure gauche. L'événement (
$e1) est ainsi correctement conservé.Une jointure entité-événement doit être une jointure droite. L'événement (
$e1) est ainsi correctement conservé.
L'exemple suivant n'est pas valide, car l'événement UDM (
$e1) se trouve à gauche, mais la jointure droite conserve le côté droit ($g1), ce qui ne respecte pas la règle selon laquelle l'événement UDM doit être conservé :// Invalid query $e1.metadata.event_type = "NETWORK_CONNECTION" $g1.graph.metadata.entity_type = "ASSET" right join $e1.principal.asset.hostname = $g1.graph.entity.asset.hostnameLes mots clés explicites
left joinetright joinde la sectioneventsne sont pas compatibles avec les règles de détection. Ils ne sont compatibles qu'avec les recherches YARA-L et les tableaux de bord. Pour effectuer une jointure externe gauche ou une anti-jointure dans une règle de détection, utilisez une condition non limitée telle que#data_table >= 0ou#data_table = 0dans la sectioncondition. Pour en savoir plus, consultez Jointures gauches dans les règles de détection.
Bonnes pratiques
Pour éviter les ralentissements et les délais d'expiration des requêtes dans les requêtes de jointure externe, utilisez des filtres spécifiques et précis.
Par exemple, une requête large comme celle-ci :
$e1.metadata.event_type = "USER_LOGIN"
$e2.metadata.event_type = "NETWORK_CONNECTION"
right join $e1.principal.hostname = $e2.principal.hostname
Vous pouvez l'optimiser en ajoutant des critères spécifiques comme suit :
$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
Vous avez encore besoin d'aide ? Obtenez des réponses de membres de la communauté et de professionnels Google SecOps.