Procedure consigliate per query in Ricerca avanzata
Si applica a:
- Microsoft Defender XDR
Applicare queste raccomandazioni per ottenere risultati più velocemente ed evitare timeout durante l'esecuzione di query complesse. Per altre informazioni su come migliorare le prestazioni della query, vedere procedure consigliate per le query di Esplora dati.
Informazioni sulle quote delle risorse della CPU
A seconda delle dimensioni, ogni tenant ha accesso a una quantità impostata di risorse CPU allocate per l'esecuzione di query di ricerca avanzate. Per informazioni dettagliate sui vari parametri di utilizzo, vedere Le quote di ricerca avanzate e i parametri di utilizzo.
Dopo aver eseguito la query, è possibile visualizzare il tempo di esecuzione e l'utilizzo delle risorse (basso, medio, alto). Alto indica che l'esecuzione della query ha richiesto più risorse e potrebbe essere migliorata per restituire risultati in modo più efficiente.
I clienti che eseguono regolarmente più query devono tenere traccia dell'utilizzo e applicare le indicazioni sull'ottimizzazione in questo articolo per ridurre al minimo le interruzioni risultanti dal superamento delle quote o dei parametri di utilizzo.
Vedere Ottimizzazione delle query KQL per visualizzare alcuni dei modi più comuni per migliorare le query.
Suggerimenti generali per l'ottimizzazione
Dimensiona le nuove query: se si sospetta che una query restituirà un set di risultati di grandi dimensioni, valutarla prima usando l'operatore count. Usare limit o il relativo sinonimo
take
per evitare set di risultati di grandi dimensioni.Applica filtri in anticipo: applica filtri temporali e altri filtri per ridurre il set di dati, in particolare prima di usare funzioni di trasformazione e analisi, ad esempio substring(), replace(), trim(), toupper() o parse_json(). Nell'esempio seguente viene usata la funzione di analisi extractjson() dopo che gli operatori di filtro hanno ridotto il numero di record.
DeviceEvents | where Timestamp > ago(1d) | where ActionType == "UsbDriveMount" | where DeviceName == "user-desktop.domain.com" | extend DriveLetter = extractjson("$.DriveLetter", AdditionalFields)
Contiene battute: per evitare la ricerca di sottostringhe all'interno di
contains
parole inutilmente, usare l'operatorehas
anziché . Informazioni sugli operatori stringaCercare colonne specifiche: cercare in una colonna specifica anziché eseguire ricerche full-text in tutte le colonne. Non usare
*
per controllare tutte le colonne.Distinzione tra maiuscole e minuscole per velocità: le ricerche con distinzione tra maiuscole e minuscole sono più specifiche e in genere più efficienti. I nomi degli operatori stringa con distinzione tra maiuscole e minuscole, ad
has_cs
esempio econtains_cs
, terminano in genere con_cs
. È anche possibile usare l'operatore==
equals con distinzione tra maiuscole e minuscole anziché=~
.Analizza, non estrarre: quando possibile, usa l'operatore di analisi o una funzione di analisi come parse_json(). Evitare l'operatore
matches regex
stringa o la funzione extract(), che usano entrambe l'espressione regolare. Riservare l'uso di espressioni regolari per scenari più complessi. Altre informazioni sull'analisi delle funzioniFiltrare le tabelle e non le espressioni: non filtrare in base a una colonna calcolata se è possibile filtrare in base a una colonna di tabella.
Nessun termine di tre caratteri: evitare di confrontare o filtrare usando termini con tre caratteri o meno. Questi termini non sono indicizzati e la loro corrispondenza richiederà più risorse.
Progetto in modo selettivo: consente di comprendere più facilmente i risultati proiettando solo le colonne necessarie. Anche la proiezione di colonne specifiche prima dell'esecuzione di join o operazioni simili consente di migliorare le prestazioni.
Ottimizzare l'operatore join
L'operatore join unisce le righe da due tabelle in base ai valori corrispondenti nelle colonne specificate. Applicare questi suggerimenti per ottimizzare le query che usano questo operatore.
Tabella più piccola a sinistra: l'operatore
join
corrisponde ai record nella tabella sul lato sinistro dell'istruzione join ai record a destra. Con la tabella più piccola a sinistra, sarà necessario trovare una corrispondenza per un numero inferiore di record, accelerando così la query.Nella tabella seguente si riduce la tabella
DeviceLogonEvents
a sinistra per coprire solo tre dispositivi specifici prima di aggiungerla aiIdentityLogonEvents
SID dell'account.DeviceLogonEvents | where DeviceName in ("device-1.domain.com", "device-2.domain.com", "device-3.domain.com") | where ActionType == "LogonFailed" | join (IdentityLogonEvents | where ActionType == "LogonFailed" | where Protocol == "Kerberos") on AccountSid
Usare il tipo inner join: il tipo di join predefinito o le righe innerunique-join deduplicate nella tabella a sinistra tramite la chiave di join prima di restituire una riga per ogni corrispondenza alla tabella a destra. Se nella tabella sinistra sono presenti più righe con lo stesso valore per la
join
chiave, tali righe verranno deduplicate in modo da lasciare una singola riga casuale per ogni valore univoco.Questo comportamento predefinito può escludere informazioni importanti dalla tabella a sinistra che possono fornire informazioni utili. Ad esempio, la query seguente mostrerà solo un messaggio di posta elettronica contenente un allegato specifico, anche se lo stesso allegato è stato inviato usando più messaggi di posta elettronica:
EmailAttachmentInfo | where Timestamp > ago(1h) | where Subject == "Document Attachment" and FileName == "Document.pdf" | join (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
Per risolvere questa limitazione, viene applicato il tipo inner join specificando
kind=inner
di visualizzare tutte le righe della tabella a sinistra con valori corrispondenti a destra:EmailAttachmentInfo | where Timestamp > ago(1h) | where Subject == "Document Attachment" and FileName == "Document.pdf" | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
Aggiungere record da un intervallo di tempo: durante l'analisi degli eventi di sicurezza, gli analisti cercano gli eventi correlati che si verificano nello stesso periodo di tempo. L'applicazione dello stesso approccio quando si usa
join
offre anche vantaggi per le prestazioni riducendo il numero di record da controllare.La query seguente verifica la presenza di eventi di accesso entro 30 minuti dalla ricezione di un file dannoso:
EmailEvents | where Timestamp > ago(7d) | where ThreatTypes has "Malware" | project EmailReceivedTime = Timestamp, Subject, SenderFromAddress, AccountName = tostring(split(RecipientEmailAddress, "@")[0]) | join ( DeviceLogonEvents | where Timestamp > ago(7d) | project LogonTime = Timestamp, AccountName, DeviceName ) on AccountName | where (LogonTime - EmailReceivedTime) between (0min .. 30min)
Applicare filtri di ora su entrambi i lati: anche se non si sta analizzando un intervallo di tempo specifico, l'applicazione di filtri temporali nelle tabelle sinistra e destra può ridurre il numero di record per controllare e migliorare
join
le prestazioni. La query seguente si applicaTimestamp > ago(1h)
a entrambe le tabelle in modo che unisca solo i record dell'ora precedente:EmailAttachmentInfo | where Timestamp > ago(1h) | where Subject == "Document Attachment" and FileName == "Document.pdf" | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
Usare hint per le prestazioni: usare gli hint con l'operatore
join
per indicare al back-end di distribuire il carico durante l'esecuzione di operazioni a elevato utilizzo di risorse. Altre informazioni sugli hint per l'aggiuntaAd esempio, l'hint shuffle consente di migliorare le prestazioni delle query quando si uniscono tabelle usando una chiave con cardinalità elevata, una chiave con molti valori univoci, ad
AccountObjectId
esempio nella query seguente:IdentityInfo | where JobTitle == "CONSULTANT" | join hint.shufflekey = AccountObjectId (IdentityDirectoryEvents | where Application == "Active Directory" | where ActionType == "Private data retrieval") on AccountObjectId
L'hint broadcast è utile quando la tabella a sinistra è piccola (fino a 100.000 record) e la tabella destra è estremamente grande. Ad esempio, la query seguente sta tentando di unire alcuni messaggi di posta elettronica con argomenti specifici con tutti i messaggi contenenti collegamenti nella
EmailUrlInfo
tabella:EmailEvents | where Subject in ("Warning: Update your credentials now", "Action required: Update your credentials now") | join hint.strategy = broadcast EmailUrlInfo on NetworkMessageId
Ottimizzare l'operatore summarize
L'operatore summarize aggrega il contenuto di una tabella. Applicare questi suggerimenti per ottimizzare le query che usano questo operatore.
Trova valori distinti: in generale, usare
summarize
per trovare valori distinti che possono essere ripetitivi. Può non essere necessario usarlo per aggregare colonne che non hanno valori ripetitivi.Anche se un singolo messaggio di posta elettronica può far parte di più eventi, l'esempio seguente non è un uso efficiente di perché un ID messaggio di
summarize
rete per un singolo messaggio di posta elettronica viene sempre fornito con un indirizzo mittente univoco.EmailEvents | where Timestamp > ago(1h) | summarize by NetworkMessageId, SenderFromAddress
L'operatore
summarize
può essere facilmente sostituito conproject
, generando potenzialmente gli stessi risultati e consumando meno risorse:EmailEvents | where Timestamp > ago(1h) | project NetworkMessageId, SenderFromAddress
L'esempio seguente è un uso più efficiente di
summarize
perché possono essere presenti più istanze distinte di un indirizzo mittente che invia un messaggio di posta elettronica allo stesso indirizzo del destinatario. Tali combinazioni sono meno distinte e probabilmente hanno duplicati.EmailEvents | where Timestamp > ago(1h) | summarize by SenderFromAddress, RecipientEmailAddress
Riordinare la query: sebbene
summarize
sia preferibile usare in colonne con valori ripetitivi, le stesse colonne possono anche avere cardinalità elevata o un numero elevato di valori univoci. Come l'operatorejoin
, è anche possibile applicare l'hint shuffle consummarize
per distribuire il carico di elaborazione e potenzialmente migliorare le prestazioni quando si opera su colonne con cardinalità elevata.La query seguente usa
summarize
per contare l'indirizzo di posta elettronica del destinatario distinto, che può essere eseguito in centinaia di migliaia in organizzazioni di grandi dimensioni. Per migliorare le prestazioni, incorporahint.shufflekey
:EmailEvents | where Timestamp > ago(1h) | summarize hint.shufflekey = RecipientEmailAddress count() by Subject, RecipientEmailAddress
Scenari di query
Identificare processi univoci con ID processo
Gli ID processo (PID) sono riciclati in Windows e riutilizzati per i nuovi processi. Di per sé, non possono servire come identificatori univoci per processi specifici.
Per ottenere un identificatore univoco per un computer specifico, usare l'ID processo insieme all'ora di creazione del processo. Quando si uniscono o si riepilogano i dati relativi a processi, includere le colonne per l'identificatore del computer (DeviceId
o DeviceName
), l'ID processo (ProcessId
o InitiatingProcessId
) e l'ora di creazione del processo (ProcessCreationTime
o InitiatingProcessCreationTime
)
La seguente query di esempio trova i processi che accedono a più di 10 indirizzi IP dalla porta 445 (SMB), possibilmente analizzando le condivisioni file.
Query di esempio:
DeviceNetworkEvents
| where RemotePort == 445 and Timestamp > ago(12h) and InitiatingProcessId !in (0, 4)
| summarize RemoteIPCount=dcount(RemoteIP) by DeviceName, InitiatingProcessId, InitiatingProcessCreationTime, InitiatingProcessFileName
| where RemoteIPCount > 10
La query riepiloga sia InitiatingProcessId
che InitiatingProcessCreationTime
in modo da visualizzare un singolo processo, senza combinare più processi con lo stesso ID processo.
Righe di comando di query
Esistono diversi modi per creare una riga di comando per eseguire un'attività. Ad esempio, un utente malintenzionato potrebbe fare riferimento a un file di immagine senza percorso, senza estensione di file, usando variabili di ambiente o virgolette. L'utente malintenzionato potrebbe anche modificare l'ordine dei parametri o aggiungere più virgolette e spazi.
Per creare query più durevoli sulle righe di comando, applicare le procedure seguenti:
- Identificare i processi noti , ad esempio net.exe o psexec.exe, tramite la corrispondenza nei campi del nome file, anziché applicare filtri alla riga di comando stessa.
- Analizzare le sezioni della riga di comando usando la funzione parse_command_line()
- Quando si eseguono query per gli argomenti della riga di comando, non cercare una corrispondenza esatta su più argomenti non correlati in un determinato ordine. Usare invece espressioni regolari o utilizzare più operatori distinti.
- Usare corrispondenza maiuscole/minuscole. Ad esempio, usare
=~
,in~
econtains
anziché==
,in
econtains_cs
. - Per attenuare le tecniche di offuscamento della riga di comando, prendere in considerazione la rimozione delle virgolette, la sostituzione delle virgole con spazi e la sostituzione di più spazi consecutivi con un singolo spazio. Esistono tecniche di offuscamento più complesse che richiedono altri approcci, ma queste modifiche possono aiutare ad affrontare quelle comuni.
Gli esempi seguenti illustrano diversi modi per costruire una query che cerca il file net.exe per arrestare il servizio firewall "MpsSvc":
// Non-durable query - do not use
DeviceProcessEvents
| where ProcessCommandLine == "net stop MpsSvc"
| limit 10
// Better query - filters on file name, does case-insensitive matches
DeviceProcessEvents
| where Timestamp > ago(7d) and FileName in~ ("net.exe", "net1.exe") and ProcessCommandLine contains "stop" and ProcessCommandLine contains "MpsSvc"
// Best query also ignores quotes
DeviceProcessEvents
| where Timestamp > ago(7d) and FileName in~ ("net.exe", "net1.exe")
| extend CanonicalCommandLine=replace("\"", "", ProcessCommandLine)
| where CanonicalCommandLine contains "stop" and CanonicalCommandLine contains "MpsSvc"
Inserire dati da origini esterne
Per incorporare elenchi lunghi o tabelle di grandi dimensioni nella query, usare l'operatore externaldata per inserire dati da un URI specificato. È possibile ottenere dati da file in formato TXT, CSV, JSON o altri formati. L'esempio seguente mostra come è possibile utilizzare l'elenco completo di hash SHA-256 malware forniti da MalwareBazaar (abuse.ch) per controllare gli allegati nei messaggi di posta elettronica:
let abuse_sha256 = (externaldata(sha256_hash: string)
[@"https://bazaar.abuse.ch/export/txt/sha256/recent/"]
with (format="txt"))
| where sha256_hash !startswith "#"
| project sha256_hash;
abuse_sha256
| join (EmailAttachmentInfo
| where Timestamp > ago(1d)
) on $left.sha256_hash == $right.SHA256
| project Timestamp,SenderFromAddress,RecipientEmailAddress,FileName,FileType,
SHA256,ThreatTypes,DetectionMethods
Analizzare le stringhe
Esistono varie funzioni che è possibile usare per gestire in modo efficiente le stringhe che richiedono l'analisi o la conversione.
Stringa | Funzione | Esempio di utilizzo |
---|---|---|
Righe di comando | parse_command_line() | Estrarre il comando e tutti gli argomenti. |
Percorsi | parse_path() | Estrarre le sezioni di un percorso di file o cartella. |
Numeri di versione | parse_version() | Decostruire un numero di versione con un massimo di quattro sezioni e fino a otto caratteri per sezione. Usare i dati analizzati per confrontare l'età della versione. |
Indirizzi IPv4 | parse_ipv4() | Convertire un indirizzo IPv4 in un numero intero lungo. Per confrontare gli indirizzi IPv4 senza convertirli, usare ipv4_compare(). |
Indirizzi IPv6 | parse_ipv6() | Convertire un indirizzo IPv4 o IPv6 nella notazione IPv6 canonica. Per confrontare gli indirizzi IPv6, usare ipv6_compare(). |
Per informazioni su tutte le funzioni di analisi supportate, leggere le informazioni sulle funzioni stringa Kusto.
Nota
Alcune tabelle di questo articolo potrebbero non essere disponibili in Microsoft Defender per endpoint. Attivare Microsoft Defender XDR per cercare le minacce usando più origini dati. È possibile spostare i flussi di lavoro di ricerca avanzati da Microsoft Defender per endpoint a Microsoft Defender XDR seguendo la procedura descritta in Eseguire la migrazione di query di ricerca avanzate da Microsoft Defender per endpoint.
Argomenti correlati
- Documentazione del linguaggio di query Kusto
- Quote e parametri di utilizzo
- Gestire gli errori di ricerca avanzati
- Panoramica della rilevazione avanzata
- Capire il linguaggio delle query
Consiglio
Per saperne di più, Visitare la community di Microsoft Security nella Tech Community: Tech Community di Microsoft Defender XDR.