#!/usr/bin/perl ###################################################################################### # # anon.pl # # Frisst apache-logs als stdin und gibt pseudonymisierte Logs auf stdout aus # Die IP-Adresse (oder der FQDN nach der Namensaufloesung) wird als erstes # Feld erwartet, der Username als drittes Feld. # # Der Sinn des Ganzen ist es, die fuer Statistiken interessanten Informationen # zu behalten, ohne allzu persoenliche Details dauerhaft zu speichern. # # Bei einem FQDN wird das erste Feld durch einen 10-stelligen Zufallsstring # ersetzt. Gleiche Eingaben erzeugen waehrend eines Programmlaufs auch gleiche # Ausgaben. # Bei einer IP-Adresse werden die letzten 2 Bytes auf 0 gesetzt. # # Das dritte Feld enthaelt entweder "-" oder den Usernamen bei http-auth. Das # wird auf "-" gesetzt. # Andere Felder werden unveraendet ausgegeben (Datum/Browser/GET/Referer ...) # # Das ganze sieht also dann so aus: # # Aufruf: z.B. # # :~# cat tmp.log | logresolve | anon.pl > speicher.log # :~# rm tmp.log # :~# archiviere speicher.log # # (Laufzeit: 15-20 Sekunden fuer 100000 Logzeilen auf einem PIII/733MHz/256MB) # # Eingabe Ausgabe # # xxx.example.org - Max ... rbbquopafh.example.org - - ... # blupp.example.org - - ... yqzwlyiyre.example.org - - ... # blupp.example.org - - ... yqzwlyiyre.example.org - - ... # xxx.example.org - Max ... rbbquopafh.example.org - - ... # 10.123.123.123 - - ... 10.123.0.0 - - ... # 10.123.123.5 - - ... 10.123.0.0 - - ... # # Bei FQDNs bleibt also ein Besucher anhand seines Hosts erkennbar, bei # IP-Adressen werden allerdings alle Besucher eines Class-B-Netzes # auf einen Haufen geworfen. # # Grenzen: # - Moeglicherweise reicht es nicht, nur den Hostanteil eines # FQDN zu vergessen. Sollte eine ganze Domain nur einen # Rechner beherbergen, waere allein die Domain aussagekraeftig genug. # - Die Browserkennung bleibt auch erhalten, die ist auch schon recht # eindeutig bei all den verschiedenen gebrandeten Versionen. # - Das Programm behaelt die Zuordnung Host/Zufallsstring im # Speicher, bei Logfiles mit einigen tausend Besuchern kein # Problem, bei Millionen von Besuchern wird der Speicher knapp. # - Die Zuordnung Host/Zufallsstring ist nur waehrend eines einzelnen # Programmlaufs eindeutig. Bei z.B. taeglichem Einsatz waehrend # eines logrotate verliert man die Zuordnung von einem Tag zum # naechsten. Das ist zwar schade, aber wenn wir uns die Zuordnung # irgendwo speichern wuerden, koennten wir uns die Pseudonymisierung # auch sparen. (Einweg-Verschluesselung ist hier uebrigens kein # Ausweg, der Host hat i.d.R. nur wenige verschiedene Werte, die koennte # man sehr schnell durchprobieren, wenn man das DNS-Schema eines Providers # kennt) # - Ich hab keine Ahnung, wie zufällig dieser Zufallsgenerator und dessen # initialisierung ist... # # # (C) Version 0.2 (Januar 2007) Max Berger, max@dianacht.de # Wers findet, darfs behalten, aendern, akkupfern, verwenden, verkaufen oder # verbessern und zurückschicken. # ###################################################################################### srand(); while (<>) { chop($_); # # Einzelne Felder raussuchen # @felder=split(/ /,$_); $z=0; # # Die Felder durchgehen # foreach $f (@felder){ $z++; # # Das erste Feld muessen wir aendern # if($z==1){ # # an den Punkte aufteilen # @host=split(/\./,$f); $n=@host; # # Letzen Teilstring anschaun. Isses eine Zahl, dann die letzten 2 Bytes durch # 0 ersetzen. Sinds Buchstaben, dann fuer den ersten Teil einen Zufallsstring # erzeugen, oder den alten String verwenden, wenn schonmal einer # fuer diesen Host erstellt wurde. # if( ($host[$n-1] =~ /[0-9]/ ) ) { $host[$n-1]=$host[$n-2]=0; }else{ if($zufall{"$f"} eq ""){ for($x=0;$x<10;$x++){$zufall{"$f"}=$zufall{"$f"}.chr(rand(26)+97);} } $host[0]=$zufall{"$f"}; } $f=join(".",@host); } # # Das 3. Feld enthaelt den Username, falls einer da ist. Da scheiben wir "-" # rein. # if($z==3){ $f="-"; } print "$f"; if($z != @felder) { print " "; } } print "\n"; } ############ Fertig ##############################################################