Kategorie: Reguläre Ausdrücke

  • Anonymisieren von IP-Adressen in einer Apache Log Datei mithilfe von sed

    Ziel: Man möchte die IP-Adressen in einer Apache Logdatei anonymisieren, indem man das letzte Byte oder die zwei letzten Byte der IP-Adresse durch 0 ersetzt.

    Lösungsmöglichkeit: Zeilenweises Durchsuchen der Logdatei mit dem Unix Werkzeug sed. Suchen der IP-Adressen und Ersetzen der Fundstellen mit der anonymiserten IP-Adresse.
    Z. B. soll die IP-Adresse 123.123.123.123 durch die Adresse 123.123.0.0 ersetzt werden.

    Dazu kann man folgenden Einzeiler verwenden:

    sed -i -e 's/\([0-9]\{1,3\}\)\.\([0-9]\{1,3\}\)\.\([0-9]\{1,3\}\)\.\([0-9]\{1,3\}\)/\1\.\2\.0\.0/' access_log_ssl.log

    Hier der Versuch einer Erklärung, ohne zu tief auf die regulären Ausdrücke, die in dem sed-Skript verwendet werden, einzugehen.

    Vereinfacht sieht das sed-Skript wie folgt aus:

    sed -i -e 's/SUCHMUSTER/ERSETZUNG/g' access_log.log

    -e = Parameter der angibt, dass ein sed-Script folgt
    ’s/SUCHMUSTER/ERSETZUNG/g‘ = sed-Skript
    SUCHMUSTER = Muster, nach dem gesucht werden soll
    ERSETZUNG = Text, mit dem das Suchmuster ersetzt werden soll
    access_log.log = Eingabedatei
    -i = Inplace Bearbeitung – die Änderungen werden direkt an der Eingabedatei ausgeführt

    Sollen die Änderungen in einer Kopie gespeichert werden, dann wird sed ohne den -i Parameter benutzt und die Ausgabe in eine neue Datei umgeleitet.

    sed -e 's/SUCHMUSTER/ERSETZUNG/g' access_log.log > bearbeitet.log

    In diesem Fall bleibt die Eingabedatei unverändert und die Änderungen werden in der Datei bearbeitet.log gepeichert.

    Jetzt zu dem konkreten Beispiel:

    Der besseren Lesbarkeit halber wurden die vielen Maskierungen mit dem Zeichen \ entfernt. Diese werden aber in dem eigentlichen sed-Skript benötigt.

    Hier das Suchmuster in unserem sed-Skript:

    ([0-9]{1,3}).([0-9]{1,3}).([0-9]{1,3}).([0-9]{1,3})

    [0-9] = Die Zeichen 0-9, also 0123456789 werden an dieser Stelle gesucht
    {1,3} = Die Zeichen [0-9] müssen 1 bis 3 mal auftreten
    ([0-9]{1,3}) = Die Klammern kennzeichnen eine Einheit in dem Suchmuster, auf die anschließend beim Ersetzten zugegriffen werden kann
    ([0-9]{1,3}).([0-9]{1,3}).([0-9]{1,3}).([0-9]{1,3}) = Das Muster muss vier Mal, getrennt duch einen Punkt, hintereinander auftreten.

    Damit hat man aber keine hundertprozentige Sicherheit, dass nur gültige IPv4 Adressen von dem Suchmuster gefunden werden. Ungültige Adressen wie z.B. 999.999.999.999 würden duch das Suchmuster auch gefunden.
    Für unseren Anwendungsfall reicht das vereinfachte Suchmuster aber vollkommen aus.

    Jetzt zu dem ersetzen Teil des sed-Skripts. Auch hier sind die Maskierungen durch das Zeichen \ weggelassen.

    \1.\2.0.0

    \1 = Die erste mit Klammern definierte Einheit im Suchmuster
    \2 = Die zweite mit Klammern definierte Einheit im Suchmuster; analog dazu könnte man noch \3 und \4 für die dritte und vierte Einheit im Suchmuster benutzen. \1.\2.\3.\4 würde die ursprüngliche IP unverändert ausgeben.
    0 = Die Ziffer 0

    Der Ersetzen-Teil des sed-Skripts bedeutet also, dass eine gefundene IP-Adresse mit ihren ersten zwei Bytes und mit einer Null als drittes und viertes Byte ersetzt werden sollen.

  • Reguläre Ausdrücke – regex, singleline, non-greedy

    Ich habe mich bisher nur so viel wie nötig mit regulären Ausdrücken beschäftigt. Der komplexe Syntax und das kryptische Aussehen der regulären Ausdrücke haben mich bisher davon abgeschreckt, reguläre Ausdrücke zu benutzen. Kürzlich hatte ich aber das Problem, in vb.net Text zwischen einer Startmarkierung und einer Endmarkierung zu suchen. Natürlich kann man das auch mit den .net Stringfunktionen erreichen, aber ich wollte es dieses Mal mit regulären Ausdrücken versuchen.

    Nach einigen Anfangsproblemen bin ich nun richtig begeistert von der Mächtigkeit der regulären Ausdrücke.

    Mein erstes Problem war, dass sich mein zu analysierender Text über mehrere Zeilen zog, also waren Zeilenumbrüche vorhanden. Ebenso zogen sich die zu extrahierenden Textstellen über mehrere Zeilen. Meine regulären Ausdrücke lieferten nicht das erwartete Ergebnis. Besser gesagt: Sie lieferten gar kein Ergebnis.

    Nach etwas Internetrecherche erfuhr ich, dass bei der Erstellung eines regex Objektes Optionen angegeben werden können. In vb.net findet sich das Ganze unter: System.Text.RegularExpressions.RegexOptions

    Ohne weiter nachzulesen, wählte ich die Option Multiline, da ich ja Text zeilenübergreifend analysieren wollte. Diese Option lieferte aber zu meiner Überraschung ebenfalls kein Ergebnis.

    Also machte ich mich daran, die Optionen näher anzuschauen. Dabei fand ich eine weitere Option: Singleline. Und genau diese muss man angeben, um das gewünschte Ergebnis zu erreichen. Ich finde das Ganze zwar etwas verwirrend: Singleline für zeilenübergreifendes Analysieren – aber Hauptsache, es funktioniert.

    Wenn man also Text in vb.net mit regulären Ausdrücken über mehrere Zeilen hinweg analysieren möchte, muss man folgende Option einstellen:

    System.Text.RegularExpressions.RegexOptions.Singleline

    Beispiel:

    Text, der analysiert werden soll:

    start text1
    text2
    text3 ende

    Regulärer Ausdruck: start(.*)ende

    Dieser Ausdruck soll einen String zwischen start und ende extrahieren.

    Ergebnis ohne die Option Singleline:

    – Es wird nichts gefunden

    Ergebnis mit der Option Singleline:

    text1
    text2
    text3

    Danach tauchte aber ein weiteres Problem auf. Bei sich mehrfach wiederholenden Mustern im Text, wurde immer die größtmögliche Ergebnismenge zurückgeliefert. Ich brauchte aber jede einzelne Ergebnismenge. Das Ganze wird an einem Beispiel deutlicher:

    Text, der analysiert werden soll:

    start text1
    text2
    text3 ende
    start text4
    text5
    text6 ende

    Der Regulärer Ausdruck: start(.*)ende liefert folgendes Ergebnis:

    text1
    text2
    text3 ende
    start text4
    text5
    text6

    Gewünscht waren aber 2 Ergebnismengen:

    Ergebnis 1:

    text1
    text2
    text3

    Ergebnis 2:

    text4
    text5
    text6

    Bei der Lösung dieses Problems stieß ich auf die Begriffe greedy (gierig) und non-greedy (nicht gierig) im Zusammenhang mit der Definition von regulären Ausdrücken. Greedy bedeutet, es wird immer die größtmögliche Ergebnismenge zurückgeliefert und non-greedy bedeutet, es wird die kleinstmögliche Ergebnismenge zurückgeliefert. Standartmäßig zeigen reguläre Ausdrücke ein „gieriges (greedy)“ Verhalten. Um ein „nicht gieriges (non-greedy)“ Verhalten zu erreichen, muss man bei der Definition von regulären Ausdrücken nach dem Quanifizierer noch ein ? anfügen.

    Beispiel:

    Text, der analysiert werden soll:

    start text1
    text2
    text3 ende
    start text4
    text5
    text6 ende

    Der reguläre Ausdruck: start(.*?)ende liefert das gewünschte Ergebnis:

    Ergebnis 1:

    text1
    text2
    text3

    Ergebnis 2:

    text4
    text5
    text6