Erstellen eines ASIM-Parsers
Benutzer des Advanced Security Information Model (ASIM) verwenden vereinheitlichende Parser anstelle von Tabellennamen in ihren Abfragen, um Daten in einem normalisierten Format anzuzeigen und alle für das Schema relevanten Daten in die Abfrage einzuschließen. Vereinheitlichende Parser verwenden wiederum quellspezifische Parser, um die spezifischen Details jeder Quelle zu verarbeiten.
Microsoft Sentinel bietet integrierte quellenspezifische Parser für viele Datenquellen. Sie können diese quellspezifischen Parser in den folgenden Situationen ändern oder entwickeln:
Wenn Ihr Gerät Ereignisse bereitstellt, die in ein ASIM-Schema passen, aber ein quellspezifischer Parser für Ihr Gerät und das relevante Schema ist in Microsoft Sentinel nicht verfügbar.
Wenn quellenspezifische ASIM-Parser für Ihr Gerät verfügbar sind, ihr Gerät jedoch Ereignisse mit einer Methode oder in einem Format sendet, die oder das nicht von den ASIM-Parsern erwartet wird. Beispiel:
Ihr Quellgerät kann so konfiguriert sein, dass Ereignisse auf nicht standardmäßige Weise gesendet werden.
Ihr Gerät hat möglicherweise eine andere Version als die vom ASIM-Parser unterstützte.
Die Ereignisse werden möglicherweise von einem zwischengeschalteten System gesammelt, geändert und weitergeleitet.
Entwicklungsprozess für benutzerdefinierte Parser
Der folgende Workflow beschreibt auf hoher Ebene das Vorgehen bei der Entwicklung eines benutzerdefinierten quellenspezifischen ASIM-Parsers:
Sammeln Sie Beispielprotokolle.
Identifizieren Sie die Schemata oder Schemas, die den von der Quelle gesendeten Ereignissen entsprechen.
Ordnen Sie die Quellereignisfelder dem identifizierten Schema oder den Schemata zu.
Entwickeln Sie einen oder mehrere ASIM-Parser für Ihre Quelle. Sie müssen für jedes Schema, das für die Quelle relevant ist, einen Parser für das Filtern und einen Parser ohne Parameter entwickeln.
Testen Sie Ihren Parser.
Stellen Sie die Parser in Ihren Microsoft Sentinel-Arbeitsbereichen bereit.
Aktualisieren Sie den relevanten vereinheitlichenden ASIM-Parser, sodass er auf den neuen benutzerdefinierten Parser verweist.
Möglicherweise möchten Sie auch Ihre Parser zur primären ASIM-Verteilung beitragen. Beigetragene Parser können auch in allen Arbeitsbereichen als integrierte Parser verfügbar gemacht werden.
Sammeln von Beispielprotokollen
Um effektive ASIM-Parser zu erstellen, benötigen Sie eine repräsentative Gruppe von Protokollen, die in den meisten Fällen das Einrichten des Quellsystems und die Verbindung mit Microsoft Sentinel erfordern. Wenn Sie nicht über das Quellgerät verfügen, können Sie mit cloudbasierten Pay-as-You-Go-Diensten viele Geräte für Entwicklung und Tests bereitstellen.
Darüber hinaus können die Suche nach den Lieferantendokumentationen und Beispielen für die Protokolle dazu beitragen, die Entwicklung zu beschleunigen und Fehler zu verringern, indem sie eine breite Protokollformatabdeckung gewährleisten.
Eine repräsentative Gruppe von Protokollen sollte Folgendes umfassen:
- Ereignisse mit unterschiedlichen Ereignisergebnissen.
- Ereignisse mit unterschiedlichen Antwortaktionen.
- Verschiedene Formate für Benutzername, Hostname und IDs und andere Felder, die eine Normalisierung des Werts erfordern.
Kartierung
Bevor Sie einen Parser entwickeln, ordnen Sie die im Quellereignis verfügbaren Informationen oder Ereignisse dem schema zu, das Sie identifiziert haben:
- Mappen Sie alle obligatorischen Felder und vorzugsweise auch empfohlene Felder zu.
- Versuchen Sie, alle Informationen, die von der Quelle verfügbar sind, normalisierten Feldern zuzuordnen. Wenn sie nicht als Teil des ausgewählten Schemas verfügbar ist, sollten Sie die Zuordnung zu Feldern in anderen Schemas in Betracht ziehen.
- Ordnen Sie Werte für Felder an der Quelle den normalisierten Werten zu, die ASIM erlaubt. Der ursprüngliche Wert wird in einem separaten Feld gespeichert, z. B. EventOriginalResultDetails.
Entwickeln von Parsern
Entwickeln Sie sowohl einen Filter- als auch einen parameterlosen Parser für jedes relevante Schema.
Ein benutzerdefinierter Parser ist eine KQL-Abfrage, die auf der Seite "Microsoft Sentinel Logs" entwickelt wurde. Die Parserabfrage besteht aus drei Teilen:
Filtern > Parsen > Felder vorbereiten
Filterung der relevanten Datensätze
Tabellen in Microsoft Sentinel enthalten oft mehrere Ereignistypen. Beispiel:
- Die Syslog-Tabelle enthält Daten aus mehreren Quellen.
- Benutzerdefinierte Tabellen können Informationen aus einer einzelnen Quelle enthalten, die mehrere Ereignistypen umfasst und für verschiedene Schemas geeignet ist.
Daher sollte ein Parser zunächst nur die für das Zielschema relevanten Datensätze herausfiltern.
Das Filtern in KQL erfolgt mithilfe des Where-Operators . Beispiel: Das Sysmon-Ereignis 1 meldet die Prozesserstellung und wird daher mit dem Schema ProcessEvent normalisiert. Das Sysmon-Ereignis 1 ist Teil der Ereignistabelle , sodass Sie den folgenden Filter verwenden würden:
Event | where Source == "Microsoft-Windows-Sysmon" and EventID == 1
Von Bedeutung
Ein Parser sollte nicht nach Zeit filtern. Die Abfrage, die den Parser verwendet, wendet einen Zeitbereich an.
Filtern nach Quelltyp mithilfe einer Watchlist
In einigen Fällen enthält das Ereignis selbst keine Informationen, die das Filtern nach bestimmten Quelltypen ermöglichen.
Beispielsweise werden Infoblox-DNS-Ereignisse als Syslog-Nachrichten gesendet und sind schwer von Syslog-Nachrichten zu unterscheiden, die von anderen Quellen gesendet werden. In solchen Fällen basiert der Parser auf einer Liste von Quellen, die die relevanten Ereignisse definiert. Diese Liste wird in der ASimSourceType-Watchlist verwaltet.
So verwenden Sie die ASimSourceType-Watchlist in Ihren Parsern:
- Fügen Sie am Anfang des Parsers die folgende Zeile ein:
let Sources_by_SourceType=(sourcetype:string){_GetWatchlist('ASimSourceType') | where SearchKey == tostring(sourcetype) | extend Source=column_ifexists('Source','') | where isnotempty(Source)| distinct Source };
- Fügen Sie einen Filter hinzu, der die Watchlist im Parserfilterabschnitt verwendet. Der Infoblox-DNS-Parser enthält z. B. Folgendes im Filterabschnitt:
| where Computer in (Sources_by_SourceType('InfobloxNIOS'))
So verwenden Sie dieses Beispiel in Ihrem Parser:
Ersetzen Sie "Computer" durch den Namen des Felds, das die Quellinformationen für Ihre Quelle enthält. Sie können dies als „Computer“ für alle Parser beibehalten, die auf Syslog basieren.
Ersetzen Sie das InfobloxNIOS-Token durch einen Wert Ihrer Wahl für Ihren Parser. Informieren Sie Parserbenutzer darüber, dass sie die ASimSourceType-Watchlist mit Ihrem ausgewählten Wert aktualisieren müssen, und die Liste der Quellen, die Ereignisse dieses Typs senden.
Filtern anhand von Parser-Parametern
Stellen Sie beim Entwickeln von Filterparsern sicher, dass Ihr Parser die Filterparameter für das relevante Schema akzeptiert, wie im Referenzartikel für dieses Schema dokumentiert. Die Verwendung eines vorhandenen Parsers als Ausgangspunkt stellt sicher, dass Ihr Parser die richtige Funktionssignatur enthält. In den meisten Fällen ist der tatsächliche Filtercode auch für filternde Parser für dasselbe Schema ähnlich.
Wenn Sie filtern, stellen Sie sicher, dass Sie:
- Filtern Sie vor dem Parsen anhand der physischen Felder. Wenn die gefilterten Ergebnisse nicht genau genug sind, wiederholen Sie den Test nach der Analyse, um Ihre Ergebnisse zu optimieren. Weitere Informationen finden Sie unter Filteroptimierung.
- Filtern Sie nicht, wenn der Parameter nicht definiert ist und trotzdem den Standardwert aufweist.
Die folgenden Beispiele zeigen, wie die Filterung für einen String-Parameter, dessen Standardwert normalerweise '*' ist, und für einen Listenparameter, dessen Standardwert normalerweise eine leere Liste ist, implementiert wird.
srcipaddr=='*' or ClientIP==srcipaddr
array_length(domain_has_any) == 0 or Name has_any (domain_has_any)
Filterungsoptimierung
Beachten Sie die folgenden Empfehlungen für die Filterung, um die Leistungsfähigkeit des Parsers zu gewährleisten:
- Führen Sie die Filterung immer nach integrierten statt nach geparsten Feldern durch. Obwohl es manchmal einfacher ist, mithilfe analysierter Felder zu filtern, wirkt sich dies erheblich auf die Leistung aus.
- Verwenden Sie Operatoren, die eine optimale Leistung liefern. Dies gilt insbesondere für „==“, „has“ und „startswith“. Die Verwendung von Operatoren wie "contains" oder "matches regex" wirkt sich auch erheblich auf die Leistung aus.
Die Empfehlungen für die Filterung zur Leistungsoptimierung lassen sich nicht immer einfach umsetzen. Beispielsweise ist die Verwendung von „has“ weniger genau als die von „contains“. In anderen Fällen ist der Abgleich mit dem integrierten Feld, z. B. SyslogMessage, weniger genau als das Vergleichen eines extrahierten Felds, z. B. DvcAction. In diesen Fällen empfiehlt es sich, für ein integriertes Feld dennoch eine Vorabfilterung mithilfe eines leistungsoptimierenden Operators durchzuführen und die Filterung nach der Analyse mit genaueren Bedingungen erneut durchzuführen.
Ein Beispiel finden Sie im folgenden Infoblox-DNS-Parserausschnitt. Der Parser überprüft zuerst, ob das SyslogMessage-Feld über den Wortclient verfügt. Der Begriff kann jedoch an einer anderen Stelle in der Nachricht verwendet werden. Nach der Analyse des Log_Type-Felds überprüft der Parser also erneut, dass das Wort 'client' tatsächlich der Wert des Felds ist.
Syslog | where ProcessName == "named" and SyslogMessage has "client"
…
| extend Log_Type = tostring(Parser[1]),
| where Log_Type == "client"
Parsen
Nachdem bei der Abfrage die relevanten Datensätze ausgewählt wurden, müssen sie möglicherweise analysiert werden. In der Regel ist das Parsen erforderlich, wenn mehrere Ereignisfelder in einem einzelnen Textfeld übermittelt werden.
Die KQL-Operatoren, mit denen die Analyse durchgeführt wird, sind nachfolgend nach ihrer Leistungsoptimierung sortiert aufgeführt. Der erste bietet die am besten optimierte Leistung, während der letzte die am wenigsten optimierte Leistung bietet.
| Bediener | BESCHREIBUNG |
|---|---|
| split | Parsen Sie eine Zeichenfolge aus delimitierten Werten. |
| CSV-Daten parsen | Analysiert eine Zeichenfolge von Werten, die als CSV-Zeile (mit durch Trennzeichen getrennten Werten) formatiert sind |
| parse | Analysiert mehrere Werte aus einer beliebigen Zeichenfolge anhand eines Musters, das ein vereinfachtes Muster mit besserer Leistung oder ein regulärer Ausdruck sein kann |
| extract_all | Analysiert einzelne Werte aus einer beliebigen Zeichenfolge anhand eines regulären Ausdrucks. extract_all weist eine ähnliche Analyseleistung auf, wenn letztere einen regulären Ausdruck verwendet. |
| Auszug | Extrahiert einen einzelnen Wert aus einer beliebigen Zeichenfolge anhand eines regulären Ausdrucks. Die Verwendung von extract bietet eine bessere Leistung als parsen oder extract_all, wenn ein einzelner Wert erforderlich ist. Die Verwendung mehrerer Extraktaktivierungen über dieselbe Quellzeichenfolge ist jedoch weniger effizient als eine einzelne Analyse oder extract_all und sollte vermieden werden. |
| parse_json | Analysiert die Werte in einer Zeichenfolge, die als JSON-Code formatiert ist. Wenn nur wenige Werte aus dem JSON-Code benötigt werden, bietet die Verwendung von Analyse, Extrakt oder extract_all eine bessere Leistung. |
| parse_xml | Analysiert die Werte in einer Zeichenfolge, die als XML-Code formatiert ist. Wenn nur wenige Werte aus dem XML-Code benötigt werden, bietet die Verwendung von Analyse, Extrakt oder extract_all eine bessere Leistung. |
Zusätzlich zum Parsen von Zeichenfolgen kann die Parsingphase eine weitere Verarbeitung der ursprünglichen Werte erfordern, einschließlich:
Formatierung und Typkonvertierung. Das Quellfeld muss möglicherweise nach dem Extrahieren so formatiert werden, dass es an das Zielschemafeld angepasst wird. Beispielsweise müssen Sie möglicherweise eine Zeichenfolge, die Datum und Uhrzeit darstellt, in ein datetime-Feld konvertieren. Funktionen wie Todatetime und Tohex sind in diesen Fällen hilfreich.
Wertsuche. Der Wert des Quellfelds muss nach der Extraktion möglicherweise der für das Zielschemafeld angegebenen Gruppe von Werten zugeordnet werden. Beispielsweise melden einige Quellen numerische DNS-Antwortcodes, während das Schema die gängigeren Textantwortcodes anordnet. Die Funktionen iff und Case können hilfreich sein, um einige Werte zuzuordnen.
Beispielsweise weist der Microsoft DNS-Parser das EventResult-Feld basierend auf der Ereignis-ID und dem Antwortcode mithilfe einer iff-Anweisung wie folgt zu:
extend EventResult = iff(EventId==257 and ResponseCode==0 ,'Success','Failure')Verwenden Sie für mehrere Werte Datentabellen und -nachschlagevorgänge, wie im gleichen DNS-Parser veranschaulicht:
let RCodeTable = datatable(ResponseCode:int,ResponseCodeName:string) [ 0, 'NOERROR', 1, 'FORMERR'....]; ... | lookup RCodeTable on ResponseCode | extend EventResultDetails = case ( isnotempty(ResponseCodeName), ResponseCodeName, ResponseCode between (3841 .. 4095), 'Reserved for Private Use', 'Unassigned')
Zuordnen von Werten
In vielen Fällen muss der extrahierte Originalwert normalisiert werden. In ASIM verwendet eine MAC-Adresse beispielsweise Doppelpunkte als Trennzeichen, während die Quelle möglicherweise eine durch Bindestriche getrennte MAC-Adresse senden kann. Der primäre Operator zum Transformieren von Werten wird zusammen mit einer breiten Gruppe von KQL-Zeichenfolgen-, numerischen und Datumsfunktionen erweitert, wie im abschnitt "Analysieren" oben gezeigt.
Verwenden Sie die Anweisungen „case“, „iff“ und „lookup“, wenn Sie eine Wertemenge den Werten zuordnen müssen, die für das Zielfeld zulässig sind.
Wenn jeder Quellwert einem Zielwert zugeordnet wird, definieren Sie die Zuordnung mithilfe des Operators „datatable“ und mit „lookup“. Beispiel:
let NetworkProtocolLookup = datatable(Proto:real, NetworkProtocol:string)[
6, 'TCP',
17, 'UDP'
];
let DnsResponseCodeLookup=datatable(DnsResponseCode:int,DnsResponseCodeName:string)[
0,'NOERROR',
1,'FORMERR',
2,'SERVFAIL',
3,'NXDOMAIN',
...
];
...
| lookup DnsResponseCodeLookup on DnsResponseCode
| lookup NetworkProtocolLookup on Proto
Beachten Sie, dass die Suche auch dann nützlich und effizient ist, wenn die Zuordnung nur zwei mögliche Werte hat.
Wenn die Zuordnungsbedingungen komplexer sind, verwenden Sie die iff - oder Fallfunktionen . Die iff-Funktion ermöglicht die Zuordnung von zwei Werten:
| extend EventResult =
iff(EventId==257 and ResponseCode==0,'Success','Failure’)
Die case-Funktion unterstützt mehrere Zielwerte. Das folgende Beispiel zeigt, wie Sie lookup und case kombinieren können. Im obigen Nachschlagebeispiel wird ein leerer Wert im Feld DnsResponseCodeName zurückgegeben, wenn der Nachschlagewert nicht gefunden wird. Im folgenden Fallbeispiel wird es durch das Ergebnis des Nachschlagevorgangs erweitert, falls verfügbar, und es werden andernfalls zusätzliche Bedingungen angegeben.
| extend DnsResponseCodeName =
case (
DnsResponseCodeName != "", DnsResponseCodeName,
DnsResponseCode between (3841 .. 4095), 'Reserved for Private Use',
'Unassigned'
)
Vorbereiten aller Felder im Resultset
Der Parser muss die Felder im Resultset vorbereiten, um sicherzustellen, dass die normalisierten Felder verwendet werden.
Die folgenden KQL-Operatoren werden verwendet, um Felder in Ihrem Resultset vorzubereiten:
| Bediener | BESCHREIBUNG | Wann in einem Parser verwenden? |
|---|---|---|
| Projektumbenennung | Benennt Felder um. | Wenn ein Feld im tatsächlichen Ereignis vorhanden ist und nur umbenannt werden muss, verwenden Sie die Projektumbenennung. Das umbenannte Feld verhält sich weiterhin wie ein integriertes Feld, und Vorgänge im Feld weisen eine wesentlich bessere Leistung auf. |
| project-away | Entfernt Felder. | Verwenden Sie „project-away“ für bestimmte Felder, die Sie aus dem Resultset entfernen möchten. Es wird empfohlen, die ursprünglichen Felder nicht zu entfernen, die nicht aus dem Resultset normalisiert werden, es sei denn, sie erzeugen Verwirrung oder sind sehr groß und können Leistungsauswirkungen haben. |
| Projekt | Wählt Felder aus, die vorher existierten oder als Teil der Anweisung erstellt wurden und entfernt alle anderen Felder. | Nicht für die Verwendung in einem Parser empfohlen, da der Parser keine anderen Felder entfernen sollte, die nicht normalisiert sind. Wenn Sie bestimmte Felder entfernen müssen, z. B. temporäre Werte, die während des Parsens verwendet werden, verwenden Sie 'project-away', um sie aus den Ergebnissen zu löschen. |
| extend | Fügen Sie Aliase hinzu. | Abgesehen von der Rolle beim Generieren berechneter Felder wird der Erweiterungsoperator auch zum Erstellen von Aliasen verwendet. |
Verarbeiten von Parsingvarianten
In vielen Fällen enthalten Ereignisse in einem Ereignisstream Varianten, die eine andere Analyselogik erfordern. Um verschiedene Varianten in einem einzelnen Parser zu parsen, verwenden Sie entweder Bedingungsanweisungen wie „iff“ und „case“, oder verwenden Sie eine union-Struktur.
Um union zum Behandeln mehrerer Varianten zu verwenden, erstellen Sie für jede Variante eine separate Funktion, und verwenden Sie die Union-Anweisung, um die Ergebnisse zu kombinieren:
let AzureFirewallNetworkRuleLogs = AzureDiagnostics
| where Category == "AzureFirewallNetworkRule"
| where isnotempty(msg_s);
let parseLogs = AzureFirewallNetworkRuleLogs
| where msg_s has_any("TCP", "UDP")
| parse-where
msg_s with networkProtocol:string
" request from " srcIpAddr:string
":" srcPortNumber:int
…
| project-away msg_s;
let parseLogsWithUrls = AzureFirewallNetworkRuleLogs
| where msg_s has_all ("Url:","ThreatIntel:")
| parse-where
msg_s with networkProtocol:string
" request from " srcIpAddr:string
" to " dstIpAddr:string
…
union parseLogs, parseLogsWithUrls…
Um doppelte Ereignisse und übermäßige Verarbeitung zu vermeiden, stellen Sie sicher, dass jede Funktion zuerst die Ereignisse filtert, die sie analysieren soll, und dabei systemeigene Felder verwendet. Bei Bedarf können Sie auch vor „union“ in jeder Verzweigung die Anweisung „project-away“ verwenden.
Bereitstellen von Parsern
Sie können Parser manuell bereitstellen, indem Sie sie auf die Azure Monitor-Protokollseite kopieren und anschließend die Abfrage als Funktion speichern. Diese Methode empfiehlt sich bei Tests. Weitere Informationen finden Sie unter Erstellen einer Funktion.
Um eine große Anzahl von Parsern bereitzustellen, empfehlen wir die Verwendung von Parser-ARM-Vorlagen wie folgt:
Erstellen Sie eine YAML-Datei basierend auf der relevanten Vorlage für jedes Schema, und fügen Sie Ihre Abfrage in diese ein. Beginnen Sie mit der YAML-Vorlage, die für Ihren Schematyp und Parsertyp relevant ist, ob mit Filter oder ohne Parameter.
Verwenden Sie den ASIM Yaml zu ARM-Vorlagenkonverter, um Ihre YAML-Datei in eine ARM-Vorlage zu konvertieren.
Wenn Sie ein Update bereitstellen, löschen Sie ältere Versionen der Funktionen mithilfe des Portals oder des PowerShell-Tools zum Löschen von Funktionen.
Stellen Sie Ihre Vorlage über das Azure-Portal oder PowerShell bereit.
Sie können auch mehrere Vorlagen mit einem einzelnen Bereitstellungsprozess kombinieren, indem Sie verknüpfte Vorlagen verwenden.