Erstellen eines ASIM-Parsers
Benutzer des erweiterten Sicherheitsinformationsmodells (Advanced Security Information Model, ASIM) verwenden vereinheitlichende Parser anstelle von Tabellennamen in ihren Abfragen, um Daten in einem normalisierten Format anzeigen und alle für das Schema relevanten Daten in die Abfrage einschließen zu können. Vereinheitlichende Parser verwenden wiederum quellenspezifische Parser, um die spezifischen Details der einzelnen Quellen zu verarbeiten.
Microsoft Sentinel bietet integrierte quellenspezifische Parser für viele Datenquellen. In den folgenden Situationen kann es sinnvoll sein, diese quellenspezifischen Parser zu ändern oder zu entwickeln:
Ihr Gerät liefert Ereignisse, die einem ASIM-Schema entsprechen, aber es steht kein quellenspezifischer Parser für Ihr Gerät und das entsprechende Schema in Microsoft Sentinel zur Verfügung.
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 von Beispielprotokollen.
Identifizieren Sie die Schemas, die den von der Quelle gesendeten Ereignissen entsprechen.
Ordnen Sie die Quellereignisfelder dem oder den identifizierten Schema(s) zu.
Entwickeln Sie (mindestens) einen 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 Ihrem Microsoft Sentinel-Arbeitsbereich 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 das Quellgerät nicht zur Verfügung steht, können Sie über Clouddienste mit nutzungsbasierter Bezahlung 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.
Zuordnung
Bevor Sie einen Parser entwickeln, ordnen Sie die in den Quellereignissen verfügbaren Informationen dem von Ihnen identifizierten Schema zu:
- Ordnen 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 sind, 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 von ASIM zulässig sind. 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 Protokolle-Seite von Microsoft Sentinel entwickelt wurde. Die Parserabfrage besteht aus drei Teilen:
Filtern > Parsen > Vorbereiten von Feldern
Filterung der relevanten Datensätze
Tabellen in Microsoft Sentinel enthalten oft mehrere Ereignistypen. Zum 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.
Die Filterung in KQL erfolgt anhand des Operators where. Beispiel: Das Sysmon-Ereignis 1 meldet die Prozesserstellung und wird daher mit dem Schema ProcessEvent normalisiert. Das Sysmon-Ereignis 1 ist Teil der Tabelle Event, deshalb würden Sie den folgenden Filter verwenden:
Event | where Source == "Microsoft-Windows-Sysmon" and EventID == 1
Wichtig
Ein Parser sollte nicht nach Zeit gefiltert werden. 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 würden.
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 Watchlist „ASimSourceType“ gepflegt.
So verwenden Sie die Watchlist „ASimSourceType“ 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 Abschnitt zur Parserfilterung 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 „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 die Benutzer des Parsers, dass sie die ASimSourceType-Watchlist mit dem von Ihnen ausgewählten Wert und die Liste der Quellen aktualisieren müssen, die Ereignisse dieses Typs senden.
Filtern anhand von Parser-Parametern
Wenn Sie filternde Parser entwickeln, stellen Sie sicher, dass Ihr Parser die Filterparameter für das entsprechende 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 dem Parsen, um Ihre Ergebnisse zu verfeinern. Weitere Informationen finden Sie unter Optimierung der Filterung.
- Führen Sie keine Filterung durch, wenn der Parameter nicht definiert ist und noch 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, mit geparsten Feldern zu filtern, beeinträchtigt dies die Leistung erheblich.
- Verwenden Sie Operatoren, die eine optimale Leistung liefern. Dies gilt insbesondere für „==“, „has“ und „startswith“. Auch die Verwendung von Operatoren wie „contains“ oder „matches regex“ wirkt sich 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 des integrierten Felds, z. B. „SyslogMessage“, weniger genau als der Vergleich 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 dafür finden Sie im folgenden Infoblox DNS Parser-Schnipsel. Der Parser überprüft zunächst, ob das SyslogMessage-Feld das Wort „client“ enthält. Der Begriff kann jedoch an einer anderen Stelle der Nachricht verwendet werden, sodass der Parser nach dem Analysieren des Felds „Log_Type“ erneut überprüft, ob das Wort „client“ tatsächlich der Wert des Felds war.
Syslog | where ProcessName == "named" and SyslogMessage has "client"
…
| extend Log_Type = tostring(Parser[1]),
| where Log_Type == "client"
Analyse
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 Operator bietet die am besten optimierte Leistung, der letzte die am wenigsten optimierte Leistung.
Operator | BESCHREIBUNG |
---|---|
split | Parsen Sie eine Zeichenfolge aus delimitierten Werten. |
parse_csv | 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“ hat eine ähnliche Leistung wie „parse“, wenn bei letzterem ein regulärer Ausdruck verwendet wird. |
extract | Extrahiert einen einzelnen Wert aus einer beliebigen Zeichenfolge anhand eines regulären Ausdrucks. Die Verwendung von „extract“ ermöglicht eine bessere Leistung als die von „parse“ oder „extract_all“, wenn ein einzelner Wert abgefragt werden soll. Die Verwendung mehrerer Aktivierungen von „extract“ für dieselbe Quellenzeichenfolge ist jedoch deutlich weniger effizient als eine einzige Aktivierung von „parse“ oder „extract_all“ und sollte daher 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 „parse“, „extract“ 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 „parse“, „extract“ oder „extract_all“ eine bessere Leistung. |
Neben der Analyse der Zeichenfolge kann in der Analysephase eine weitere Verarbeitung der ursprünglichen Werte erforderlich sein, beispielsweise:
Formatierung und Typkonvertierung: Das Quellfeld muss nach der Extraktion möglicherweise so formatiert werden, dass es dem Zielschemafeld entspricht. Beispielsweise müssen Sie möglicherweise eine Zeichenfolge, die Datum und Uhrzeit darstellt, in ein datetime-Feld konvertieren. In diesen Fällen sind Funktionen wie „todatetime“ und „tohex“ hilfreich.
Wertsuche: Der Wert des Quellfelds muss nach der Extraktion möglicherweise der für das Zielschemafeld angegebenen Gruppe von Werten zugeordnet werden. Einige Quellen melden z. B. numerische DNS-Antwortcodes, während das Schema die allgemeineren Textantwortcodes vorschreibt. Die Funktionen „iff“ und „case“ können nützlich sein, um einige wenige Werte zuzuordnen.
Beispielsweise weist der Microsoft DNS-Parser das Feld „EventResult“ basierend auf der Ereignis-ID und dem Antwortcode mithilfe einer iff-Anweisung zu:
extend EventResult = iff(EventId==257 and ResponseCode==0 ,'Success','Failure')
Verwenden Sie für mehrere Werte „datatable“ und „lookup“, wie im selben DNS-Parser gezeigt:
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 wichtigste Operator für die Umwandlung von Werten ist „extend“, neben einer Vielzahl von KQL-Zeichenfolgen-, numerischen und Datumsfunktionen (siehe Abschnitt zum Parsing weiter oben).
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 wurde, 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 das Lookup nützlich und effizient ist, wenn die Zuordnung nur zwei mögliche Werte aufweist.
Bei komplexeren Zuordnungsbedingungen verwenden Sie die Funktionen iff oder case. Mit der Funktion iff können Sie zwei Werte zuordnen:
| extend EventResult =
iff(EventId==257 and ResponseCode==0,'Success','Failure’)
Die Funktion case unterstützt mehr als zwei Zielwerte. Das folgende Beispiel zeigt, wie Sie lookup und case kombinieren können. Das obige lookup-Beispiel gibt einen leeren Wert im Feld „DnsResponseCodeName“ zurück, wenn der lookup-Wert nicht gefunden wird. Das case-Beispiel erweitert dies, indem das Ergebnis der Operation lookup verwendet wird (falls vorhanden) und andernfalls zusätzliche Bedingungen angegeben werden.
| extend DnsResponseCodeName =
case (
DnsResponseCodeName != "", DnsResponseCodeName,
DnsResponseCode between (3841 .. 4095), 'Reserved for Private Use',
'Unassigned'
)
Vorbereiten von Feldern im Resultset
Der Parser muss die Felder im Resultset vorbereiten, um sicherzustellen, dass die normalisierten Felder verwendet werden.
Zur Vorbereitung von Feldern in Ihren Ergebnissen werden die folgenden KQL-Operatoren verwendet:
Operator | BESCHREIBUNG | Verwendung in einem Parser |
---|---|---|
project-rename | Benennt Felder um. | Wenn ein Feld im aktuellen Ereignis vorhanden ist und nur umbenannt werden muss, verwenden Sie „project-rename“. Das umbenannte Feld verhält sich weiterhin wie ein integriertes Feld, und Vorgänge für das Feld weisen eine wesentlich bessere Leistung auf. |
project-away | Entfernt Felder. | Verwenden Sie „project-away“ für bestimmte Felder, die Sie aus der Ergebnismenge entfernen möchten. Es wird empfohlen, die nicht normalisierten Originalfelder in der Ergebnismenge beizubehalten. Sie sollten nur dann entfernt werden, wenn sie für Verwirrung sorgen oder sehr groß sind und möglicherweise die Leistung beeinträchtigen. |
project | Wählt Felder aus, die vorher existierten oder als Teil der Anweisung erstellt wurden und entfernt alle anderen Felder. | Wird nicht für die Verwendung in einem Parser empfohlen, da der Parser keine anderen Feldern entfernen soll, die nicht normalisiert wurden. Wenn Sie bestimmte Felder entfernen müssen, z. B. temporäre Werte, die beim Parsen verwendet werden, verwenden Sie zum Entfernen der Felder aus den Ergebnissen „project-away“. |
extend | Fügen Sie Aliase hinzu. | Neben seiner Rolle beim Generieren berechneter Felder wird der Operator „extend“ auch zum Erstellen von Aliasen verwendet. |
Verarbeiten von Analysevarianten
In vielen Fällen enthalten Ereignisse in einem Ereignisstream Varianten, die eine andere Analyselogik erfordern. Um verschiedene Varianten in einem einzelnen Parser zu analysieren, verwenden Sie entweder Bedingungsanweisungen wie „iff“ und „case“, oder verwenden Sie eine union-Struktur.
Wenn Sie „union“ verwenden möchten, um mehrere Varianten zu verarbeiten, erstellen Sie für jede Variante eine eigene Funktion und kombinieren Sie die Ergebnisse mit der union-Anweisung:
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 eine übermäßige Verarbeitung zu vermeiden, stellen Sie sicher, dass jede Funktion zunächst nur die zu analysierenden Ereignisse mithilfe nativer Felder filtert. Bei Bedarf können Sie auch vor der Vereinigung in jeder Verzweigung einen project-away verwenden.
Bereitstellen von Parsern
Sie können Parser manuell bereitstellen, indem Sie sie auf die Azure Monitor-Seite „Protokolle“ kopieren und 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 Schema- und Parsertyp relevant ist, filternd oder parameterlos.
Verwenden Sie den ASIM-Yaml-zu-ARM-Vorlagen-Konverter, um Ihre YAML-Datei in eine ARM-Vorlage zu konvertieren.
Wenn Sie ein Update bereitstellen, löschen Sie ältere Versionen der Funktionen über das Portal oder das PowerShell-Tool zum Löschen von Funktionen.
Stellen Sie Ihre Vorlage über das Azure-Portal oder PowerShell bereit.
Sie können auch mehrere Vorlagen zu einem einzigen Bereitstellungsprozess kombinieren, indem Sie verknüpfte Vorlagen verwenden.