|
Fortgeschrittene CGI-ProgrammierungSession-TrackingEin Problem bei CGI-Programmen ist, daß es sich bei HTTP um einzustandsloses Protokoll handelt. Bei jeder Interaktion wird ein CGI-Programmaufs Neue aufgerufen. Ohne besondere Maßnahmen ist nur eine Anfrage desBrowsers, gefolgt von einer Antwort des CGI-Programms, möglich. So lassen sich viele Anwendungen nicht realisieren. Stellen Sie sich ein Bestellsystem vor. Dieses erlaubt es, Waren in einen virtuellen Warenkorb zu stellen. Dieser Vorgang kann sich über mehrere Aufrufe des CCI-Programms erstrecken und wird entweder mit einem Abbruch der Aktion oder mit dem "Bezahlen" an der "Kasse" abgeschlossen. Ein solches System ist jedoch nicht durch einfache Frage/Antwort-Folgen zu realisieren.Man muß also Methoden finden, mit denen der Zustand zwischen mehreren Aufrufen des CGI-Programms erhalten bleibt, und die ein zustandsloses (stateless) Protokoll nutzen um ein zustandsbehaftetes (stateful) Protokoll darüber implementieren.Ein CGI-Programm selbst kann zunächst keinen Zustand halten. Wir wollen aber die mehrfache Ausführung eines CGI-Programms als ein virtuelles Programm auffassen, das bei jedem CGI-Aufruf in einen neuen Zustand übergeht. Ein einfaches Beispiel: Bei jedem Aufruf des CGI-Programms soll eine Zahl (Session-Kennung) angezeigt werden. Diese Zahl ist bei jedem neuen Aufruf zu inkrementieren. Jeder Browser bzw. Benutzer soll eine eigene Kennung besitzen, so daß es nicht ausreicht, einen globalen Zähler serverseitig zu erhöhen. Der Zähler repräsentiert den Session-Zustand. Diesen müssen wir jeweils zwischen zwei aufeinanderfolgenden Aufrufen des CCI-Programms erhalten. Da der Zustand pro
Browser bzw. User gilt, muß dieser mehrfach gespeichert werden. Zur Speicherung
des Zustands gibt es zwei Möglichkeiten:
- Der komplette Zustand wird clientseitig gespeichert. Hierbei kann er in
einer Browsersitzung versteckt sein oder aber auch in Form von Cookies auf der
Festplatte des Browsers liegen.
- Der Zustand aller Clients wird serverseitig gespeichert. Es ist dabei notwendig,
bei den Clients noch eine minimale Zustandsinformation zu hinterlegen. Diese Informationsmenge wird auch Sitzungsschlüssel genannt.
Die clientseitige Speicherung des gesamten Zustandes hat den Vorteil, daß die
Informationen vieler Clientsitzungen auch bei den Clients gespeichert sind und
somit für den Server keinen Speicherbedarf bedeuten. Ein Nachteil ist, daß die
Information auf der Clientseite eingesehen oder sogar verändert werden kann.
Da der Zustand bei jeder Interaktion übertragen werden muß, kann dies bei
vielen gleichzeitig aktiven Clients Server und Netz belasten. Ein dritter Nachteil
ist, daß keine Statistiken über die einzelnen Zustände herzustellen sind, da die
Info bei den Clients gespeichert ist.
Ein Vorteil der serverseitigen Speicherung ist, daß die gesamte Information über
alle Clients sicher beim Server aufgehoben ist und auch analysiert werden kann.
Positiv ist weiterhin, daß die Zustandsinformation nicht mehr komplett beim User
gespeichert wird. Im Folgenden werden einige Möglichkeiten der Zustandsspeicherung
vorgestellt.
Zustandsspeicherung über PATH_INFO
Eine Möglichkeit, den Zustand zu erhalten, benutzt die URL. Am Anschluß an den Namen
des CGI-Scriptes wird die URL fortgeführt. In obigem Beispiel wäre dies:
https://www.netzmafia.de/cgi-bin/state.pl/1
https://www.netzmafia.de/cgi-bin/state.pl/2
https://www.netzmafia.de/cgi-bin/state.pl/3
usw.
Hier sind /l, /2 und /3 zusätzliche Pfadinformationen, die den
Zustand repräsentieren. Innerhalb derselben Browsersitzung kann jeweils durch
Anklicken der nächsten dynamisch erzeugten URL zum nächsten Zustand gewechselt
werden. Ein Programm gelangt an diese Pfadinformation durch das Auslesen der
Umgebungsvariablen PATH_INFO. Wie das geschieht, zeigt folgendes
Programm, das auch noch einen weiteren Kniff demonstriert. Über die Umgebungsvariablen
SERVER_NAME und SCRIPT_NAME kann man automatisch den WWW-Server und
den Pfad zum Skript ermitteln. Damit sind diesbezüglich keine Änderungen
von Hand nötig, wenn das Skript auf einem anderen Server laufen soll. Die komplette
URL des Skripts ergibt sich aus
'https://' . $ENV{'SERVER_NAME'} . $ENV{'SCRIPT_NAME'};
#!/usr/bin/perl
# Zustandserhaltung mit PATH_INFO ohne CGI-Modul.
use strict;
use constant INITSTATE => 1;
my $url = $ENV{'SERVER_NAME'} . $ENV{'SCRIPT_NAME'};
my $state = retrieve_state();
my $nextstate = compute_next_state($state);
my $saveaction = save_state($nextstate);
# Tue etwas abhaengig von $state:
print "Content-type: text/html\n\n";
print "<HTHL>", "\n";
print "<HEAD><TITLE>Status mit Pathinfo</TITLE></HEAD>\n";
print "<BODY>\n";
print "<B>Zustand: ", $state,"</B><P>";
print $saveaction;
print "</BODY></HTML>\n";
sub retrieve_state
{
my $state = $ENV{'PATH_INFO'} || INITSTATE;
$state =~ s/^\///;
return $state;
}
sub compute_next_state
{
my $current_state = shift;
return $current_state + 1;
}
sub save_state
{
my $newstate = shift;
my $send_me_back = "<A HREF=\"https://$url/$newstate\"> [Weiter] </A>";
return $send_me_back;
}
Verwendet man das Perl-Modul CGI, wird das Programm einfacher zu schreiben,
man sieht aber nicht mehr so genau, was geschieht:
#!/usr/bin/perl
#Zustandserhaltung mit PATH_INFO.
use strict;
use CGI qw(:standard);
use constant INITSTATE => 1;
my $state = retrieve_state();
my $nextstate = compute_next_state($state);
my $saveaction = save_state($nextstate);
# Tue etwas abhaengig von $state:
print header;
print start_html('Status mit Pathinfo');
print '<B>Zustand: ', $state,'</B><P>';
print $saveaction;
print end_html;
sub retrieve_state
{
my $state = $ENV{'PATH_INFO'} || INITSTATE;
$state =~ s/^\///;
return $state;
}
sub compute_next_state
{
my $current_state = shift;
return $current_state + 1;
}
sub save_state
{
my $newstate = shift;
my $send_me_back = a({-href => url() . "/$newstate"}, , " [Weiter] ");
return $send_me_back;
}
Nachteil dieser Lösung ist, daß beim Beenden (oder Absturz) des Browsers alle
Pfad-Info verloren ist. Weiterhin kann der Benutzer in der URL-Zeile des
Browser beliebige Angaben machen und so einen beliebigen Zustand eingeben.
Durch diese Methode des Session-Tracking können die statischen HTML-Seiten der
weiterhin von Internet-Suchmaschinen verarbeitet (indexiert) werden, was eventuell
auch nicht erwünscht ist.
URL-Coding per Parameter
Bei dieser Methode wird die SessionID an jede Internet-Adresse (Link) als Parameter angehängt (z. B. https://www.server.de/cgi-bin/forum.cgi?state=4).
Der Wert des Parameters wird dann per GET-Methode geholt. Die Seitenabrufe sehen
dann beispielsweise so aus:
https://www.netzmafia.de/cgi-bin/state.pl
https://www.netzmafia.de/cgi-bin/state.pl?state=2
https://www.netzmafia.de/cgi-bin/state.pl?state=3
usw.
Das Programm gleicht fast dem vorhergehenden, nur dass hier
statt einer Pfadangabe ein Query-String angehängt wird
- im Prinzip das, was bei einer Formulareingabe geschieht.
#!/usr/bin/perl
# Zustandserhaltung mit QUERY_STRING.
use strict;
use constant INITSTATE => 1;
my %FORM = ();
my $url = $ENV{'SERVER_NAME'} . $ENV{'SCRIPT_NAME'};
my $state = retrieve_state();
my $nextstate = compute_next_state($state);
my $saveaction = save_state($nextstate);
# Tue etwas abhaengig von $state:
print "Content-type: text/html\n\n";
print "<HTHL>", "\n";
print "<HEAD><TITLE>Status mit Query-String</TITLE></HEAD>\n";
print "<BODY>\n";
print "<B>Zustand: ", $state, "</B><P>";
print $saveaction;
print "</BODY></HTML>\n";
sub retrieve_state
{
&parse_form;
my $state = $FORM{'state'} || INITSTATE;
return $state;
}
sub compute_next_state
{
my $current_state = shift;
return $current_state + 1;
}
sub save_state
{
my $newstate = shift;
my $send_back = "<A HREF=\"https://$url?state=$newstate\"> [Weiter] </A>";
return $send_back;
}
sub parse_form
{
my ($buffer, @pairs, $pair, $name, $value);
$buffer = $ENV{'QUERY_STRING'};
@pairs = split(/&/, $buffer);
foreach $pair (@pairs)
{
($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$FORM{$name} = $value;
}
}
Auch hier die Alternative mit dem CGI-Modul. Nun fällt z. B. die
Parse-Routine weg:
#!/usr/bin/perl
# Zustandserhaltung mit QUERY_STRING.
use strict;
use CGI qw(:standard);
use constant INITSTATE => 1;
my $state = retrieve_state();
my $nextstate = compute_next_state($state);
my $saveaction = save_state($nextstate);
# Tue etwas abhaengig von $state:
print header;
print start_html('Status mit Query-String');
print '<B>Zustand: ', $state,'</B><P>';
print $saveaction;
print end_html;
sub retrieve_state
{
my $state = param('state') || INITSTATE;
return $state;
}
sub compute_next_state
{
my $current_state = shift;
return $current_state + 1;
}
sub save_state
{
my $newstate = shift;
my $send_back = a({-href => url() . "?state=$newstate"}, " [Weiter] ");
return $send_back;
}
Auch bei dieser Methode kann der Benutzer den Zustand beliebig ändern. Trotzdem
wird sie häufig verwendet. Außerdem ist bei einem Beenden/Absturz des Browsers
der Status verloren.
Zustand über Hidden-Felder in Formularen
Man codiert den Zustand innerhalb eines Formulars über Felder, die nicht angezeigt
werden (<INPUT TYPE="HIDDEN" ...>>). Sie werden vom Browser
generiert und mit Information versehen (VALUE=...). Sendet der
Benutzer das Formular zurück, wird auch der neue Zustand geliefert. Das folgende
Programm zeigt, wie es geht:
#!/usr/bin/perl
#Zustandserhaltung mit Hidden-Feldern.
use strict;
use constant INITSTATE => 1;
my %FORM = ();
my $url = 'https://' . $ENV{'SERVER_NAME'} . $ENV{'SCRIPT_NAME'};
my $state = retrieve_state();
my $nextstate = compute_next_state($state);
my $saveaction = save_state($nextstate);
# Tue etwas abhaengig von $state:
print "Content-type: text/html\n\n";
print "<HTHL>", "\n";
print "<HEAD><TITLE>Status mit hidden fields</TITLE></HEAD>\n";
print "<BODY>\n";
print "<B>Zustand: $state </B><P>";
print "<FORM METHOD=\"POST\" ACTION=\"$url\">\n";
print $saveaction;
print '<INPUT TYPE="submit" NAME="submit" VALUE=" Abschicken ">';
print "</FORM>\n";
print "</BODY></HTML>\n";
sub retrieve_state
{
&parse_form;
my $state = $FORM{'state'} || INITSTATE;
return $state;
}
sub compute_next_state
{
my $current_state = shift;
return $current_state + 1;
}
sub save_state
{
my $newstate = shift;
my $send_back = "<INPUT TYPE=\"hidden\" NAME=\"state\" VALUE=\"$newstate\">";
return $send_back;
}
sub parse_form
{
my ($buffer, @pairs, $pair, $name, $value);
if ($ENV{'REQUEST_METHOD'} eq 'GET')
{ @pairs = split(/&/, $ENV{'QUERY_STRING'}); }
elsif ($ENV{'REQUEST_METHOD'} eq 'POST')
{
@pairs = split(/&/, $buffer);
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
}
@pairs = split(/&/, $buffer);
foreach $pair (@pairs)
{
($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$FORM{$name} = $value;
}
}
Die Version mit dem CGI-Modul ist auch hier wieder kürzer:
#!/usr/bin/perl
#Zustandserhaltung mit Hidden-Feldern.
use strict;
use CGI qw(:standard);
use constant INITSTATE => 1;
my $state = retrieve_state();
my $nextstate = compute_next_state($state);
my $saveaction = save_state($nextstate);
# Tue etwas abhaengig von $state:
print header;
print start_html('Status mit Hidden Fields');
print '<B>Zustand: ', $state,'</B><P>';
print '<FORM METHOD="POST">\n';
print $saveaction;
print '<INPUT TYPE="submit" NAME="submit" VALUE=" Abschicken ">';
print '</FORM>\n';
print end_html;
sub retrieve_state
{
my $state = param('state') || INITSTATE;
return $state;
}
sub compute_next_state
{
my $current_state = shift;
return $current_state + 1;
}
sub save_state
{
my $newstate = shift;
my $send_back = '<INPUT TYPE="hidden" NAME="state" VALUE=';
$send_back .= "\"$newstate\">";
return $send_back;
}
Auch bei dieser Methode kann der Benutzer den Zustand beliebig ändern. Trotzdem
wird sie ebenso häufig wie die vorstehende verwendet. Außerdem ist auch
bei einem Beenden/Absturz des Browsers der Status verloren. Man kann zur Not den
Formularcharakter verbergen, wenn der Submit-Button durch ein Bild getarnt wird.
Ein Vorteil ist, daß sich mehr Info in der Statusvariablen verbergen läßt.
Zustand über Cookies
Cookies ("Kekse") sind Informationen in Form einfacher ASCII-Texte, die von einem WWW-Server
auf dem Rechner des Clients gespeichert und später wieder abgerufen werden können.
Der Zustand kann clientseitig gespeichert werden und bei Bedarf von der jeweiligen
Dienst-Applikation vom Client wieder abgerufen werden. Cookies sind aber in Verruf
geraten, weil sie für User-Tracking verwendet wurden. Sie werden deshalb gerne
im Browser deaktiviert und garantieren somit kein durchgängiges Session-Tracking,
obwohl sie von Netscape eigens für diesen Zweck konzipiert worden sind. Sie stellen
jedoch kein Sicherheitsrisiko dar. Session-Tracking über Cookies stellt die einfachste
Methode dar und findet auch bei vielen Anwendungen Verwendung.
Ganz Allgemein: Cookies sind Dateien, die auf dem Rechner des
Clients abgelegt und von Prozessen auf dem Server (CGI) ausgelesen
werden können. Das Anlegen eines Cookies erfolgt durch serverseitiges
Senden der entsprechende HTTP-Header an den Browser. In einem
solchen Header sind bereits alle Informationen des Cookies enthalten.
Cookies können also dazu dienen, zwischen einem serverseitigen Prozess
und einem bestimmten Client Informationen auszutauschen.
Beispielsweise füllt der Client ein Formular aus und schickt es
an den Server.Das verarbeitwende CGI-Skript schickt in der Antwortseite
einen Cookie an den Client mit. Ruft der Client innerhalb der Verfallszeit
des Cookies dieses Formular erneut auf, sorgt die im Cookie abgelegte und
nunmehr vom CGI-Skript ausgelesene Information dafür, daß der
Client das Formular bereits ausgefüllt in seinem Browser vorfindet.
Ein Browser kann nicht mehr als 300 Cookies speichern, wobei ein Cookie
nicht größer als 4 KByte sein darf. Die Anzahl der Cookies eines Servers
ist auf 20 begrenzt.
Die wichtigsten Parameter eines Cookies:
- name=wert
Dieses Paar wird bei der Abfrage des Cookies zurückgeliefert.
Der Wert darf keine Strichpunkte, Kommata, Leerzeichen, Tabulatoren
usw. enthalten.
- expires=datum
Verfallsdatum des Cookies. Fehlt dieser Wert, "lebt" das Cookie nur für
die Dauer der Browser-Session. Das Datum besitzt das Format
Wochentag, DD-Mon-YYYY HH:MM:SS GMT.
- path=pfad
Das Cookie wird an den Server gesendet, sobald auf eine Datei im
angegebenen Pfad (ausgehend von der DocumentRoot) zugegriffen wird.
Die Angabe ist optional.
- domain=domäne
Beschränkt den Abruf des Cookies auf die Server der angegebenen
Domäne. Teilangaben sind möglich, z.B. nur die Top- und Second-Level-Domain.
Die Angabe ist optional.
Das folgende
Programm zeigt, wie Cookies eingesetzt werden. Die Cookies werden netterweise schon
in der Umgebungsvariablen HTTP_COOKIE bereitgestellt. Das Setzen eines Cookies erfolgt
im HTTP-Header. Es muß daher vor der Zeile Content-type: text/html stehen! Die
Zeile zum Setzen eines Cookies hat folgenden Aufbau:
Set-Cookie: Name=Wert, expires=Datum, path=Pfad, domain=Domain
Die Felder sind also durch Strichpunkt getrennt. Wie oben angedeutet dürfen optionale
Felder auch fehlen.
#!/usr/bin/perl
#Zustandserhaltung mit Cookies.
use strict;
use constant INITSTATE => 1;
my $url = $ENV{'SERVER_NAME'} . $ENV{'SCRIPT_NAME'};
my $Cookie_Domain = $ENV{'SERVER_NAME'};
my $ExpDate = "Monday, 31-Dec-2035 23:59:59 GMT"; # cookie expire date
my $state = retrieve_state();
my $nextstate = compute_next_state($state);
save_state($nextstate);
# Tue etwas abhaengig von $state:
print "Content-type: text/html\n\n";
print "<HTHL>", "\n";
print "<HEAD><TITLE>Status mit Cookies</TITLE></HEAD>\n";
print "<BODY>\n";
print '<B>Zustand: ', $state,'</B><P>';
print "Bitte 'Reload Page' betätigen";
print "</BODY></HTML>\n";
sub retrieve_state
{
# must be before "content-type"-line
my $state = &GetMakeCookie;
return $state;
}
sub compute_next_state
{
my $current_state = shift;
return $current_state + 1;
}
sub save_state
{
my $newstate = shift;
SetCookie("State", $newstate, $ExpDate, "/", $Cookie_Domain);
}
sub SetCookie
{
my ($name, $val, $exp, $path, $dom) = @_;
print "Set-Cookie: ";
print "$name=$val, expires=$exp, path=$path, domain=$dom\n";
}
sub GetCookies
{
my %cookies;
my $cookie;
foreach $cookie (split (/; /,$ENV{'HTTP_COOKIE'}))
{
my($key) = split(/=/, $cookie);
$cookies{$key} = substr($cookie, index($cookie, "=")+1);
}
return(%cookies);
}
sub GetMakeCookie
{
my $State = '';
my %Cookies = GetCookies();
$State = $Cookies{'State'};
$State =~ s/,.*//;
# No Cookie Data? Establish one!
if ($State eq '')
{
$State = INITSTATE;
SetCookie("State", $State, $ExpDate, "/", $Cookie_Domain);
}
return($State);
}
Will man nicht nur unseren Beispiel-Status, sondern etwa eine Kunden-Kennung speichern,
sollte der Wert anders gewählt werden. Da sich der Kunde eventuell noch gar nicht
angemeldet hat, fallen Kundennummern etc. weg. Andererseits sollten die vergebenen
Kennungen einmalig sein, sonst vermischen sich zwei Bestellungen. Die folgende Variation
der Funktion GetMakeCookie schliesst so etwas nicht aus, ist aber hinreichend
variabel. Sie nimmt den aktuellen UNIX-Zeitstempel und zwei Zufallszahlen. Damit sind auch
Kunden unterscheidbar, die sich in der gleichen Sekunde anmelden. Ausserdem verschleiern die
Zufallszahlen den Zeitstempel:
sub GetMakeCookie
{
my %Cookies = ();
$Customer = '';
%Cookies = GetCookies();
$Customer = $Cookies{'Customer'};
$Customer =~ s/,.*//;
# No Cookie Data? Establish one!
if ($Customer eq '')
{
srand(time % 1000);
$Customer = int(rand(999)) . time() . $$ . int(rand(999));
SetCookie("Customer", $Customer, $ExpDate, "/", $Cookie_Domain);
}
}
Das war jetzt die händische Lösung. Dank des Moduls CGI::Cookie
ist die Cookie-Programmierung aber genauso einfach, wie die anderen gezeigten
Methoden. Das folgende Programm zeigt die Anwendung des Moduls:
#!/usr/bin/perl
#Zustandserhaltung mit Cookies.
use strict;
use CGI qw(:standard);
use CGI::Cookie;
use constant INITSTATE => 1;
my $state = retrieve_state();
my $nextstate = compute_next_state($state);
my $saveaction = save_state($nextstate);
# Tue etwas abhaengig von $state:
print header(-cookie => $saveaction);
print start_html('Status mit Cookies');
print '<B>Zustand: ', $state,'</B><P>';
print "Bitte 'Reload Page' betätigen";
print end_html;
sub retrieve_state
{
my $state = cookie(-name => 'state') || INITSTATE;
return $state;
}
sub compute_next_state
{
my $current_state = shift;
return $current_state + 1;
}
sub save_state
{
my $newstate = shift;
my $cookie = new CGI::Cookie(-name => 'state',
-value => $newstate,
-expires => '+5m');
# diesmal nur 5 Minuten Haltbarkeit
return $cookie;
}
Das Cookie wird im Header der HTTP-Antwort des Servers übermittelt. Es
darf auch höchstens einige hundert Bytes lang werden. Auch hat jeder
Bowser eine Obergrenze für die Anzahl der verwalteten Cookies. Auch
können erfahrene Benutzer durch Editieren der Datei cookies.txt
auch die Statusinformation im Cookie verändern. Da würde es nur helfen,
die Cookie-Information zu verschlüsseln oder mit einer Signatur zu versehen.
Session-Cookies
Die Session-Cookies sollen den Benutzer für die Dauer einer
Session (Terminal-Sitzung) identifizieren, z. B. bei einem
Datenbank-Interface per CGI-Skript oder auch bei einem Spiel.
Das funktioniert folgendermaßen:
- Anmeldung
Der Benutzer gibt seine Benutzerkennung und sein Passwort in ein
Web-Formular ein. Über irgendein Protokoll (NIS, LDAP, usw.)
oder eine Datenbankabfrage erfolgt die Authentifizierung.
- Session-Aufbau
Bei erfolgreicher Anmeldung erzeugt der serverseitige Prozess
eine eindeutige ID, die einmal in einem Cookie des Benutzers
und gleichzeitig in einer Datenbank auf dem Server abgelegt wird.
- Session aktiv
Solange es das Paar (ID im Cookie == ID in der Datenbank)
gibt gilt die Session als aktiv und der Benutzer als authentifiziert.
Bei jedem Kommando des Benutzers wird nicht jedesmal eine neue Authentifizierung
vorgenommen sondern lediglich geprüft ob die Session noch gültig ist.
Über ein solches Session-Cookie lassen sich beliebige andere Systeme
abwickeln, zum Beispiel ein Warenkorbsystem oder das Speichern von Spielständen:
Die im Cookie gespeicherte ID ist der primärer Schlüssel für bestimmte
Tabellen, in denen benutzerspezifische Informationen serverseitig abgelegt sind.
Alle Eingaben des Benutzers werden nicht im Cookie sondern unter der Verwendung der
ID in der Server-Datenbank gespeichert. Das Cookie enthält nur noch die
ID und ist nur in einer Browsersitzung gültig.
Problem: Erzeugen einer eindeutigen ID
Echte Zufälle gibt es in der EDV nicht. Selbst die Perl-Funktion
rand() ermittelt eine Zufallszahl aus irgendwelchen anderen
Parametern - die nicht zufällig sind. Erst wenn man einen solchen
"Zufallswert" mit MD5 oder crypt verschlüsselt,
ist die Wahrscheinlichkeit gering, daß es mehrere gleiche Ergebnisse
gibt. Es gibt verschiedene Möglichkeiten, einen eindeutigen String
zu erzeugen, z. B. das Einbeziehen der Zeit (siehe Beispiel oben). Eine
andere Möglichkeit zeigt das folgende Programmstück.
Der String time.$$ (Sekunden seit der UNIX-Epoche und die Prozess-ID)
wird mit einem zufälligen saltder Crypt-Funktion verschlüsselt
# Session-ID generieren
sub make_id
{
# salts erzeugen
my @salts = ('a'..'z');
shuffle(\@salts);
my $salt = join ("", @salts);
my $random = crypt($salt,time.$$);
return $random;
}
# Eine Menge mischen (siehe PERL-Dokumentation: fisher_yates_shuffle)
sub shuffle
{
my $array = shift;
my ($i, $j);
for ($i = @$array; --$i;)
{
$j = int (rand($i+1));
next if ($i == $j);
@$array[$i,$j] = @$array[$j,$i];
}
}
Neben crypt gibt es auch noch andere Methoden zur
Einweg-Verschlüsselung, z. B. MD5 (siehe unten).
Nicht mehr benutzte Einträge löschen
Aufgrund der Tatsache, dass es nicht möglich ist, festzustellen,
wann ein Client seinen Browser geschlossen hat, kann es vorkommen, daß
in der Session - Datenbank unbenutzte Einträge zurückbleiben.
Auf jeden Fall sollte dem Benutzer ein Button oder ein Link angeboten werden,
mit dem er sich auszuloggen kann. Das Ausloggen löscht alle Einträge
sowie den Key in der Session-Datenbank des Servers.
Was jedoch, wenn der Benutzer sich nicht ausloggt? Dann bieten sich folgende
Möglichkeiten an:
- Gültigkeitsdauer einer Session festlegen
Zu einer Session wird die Eröffnungszeit festgehalten. In jedem zur
Session gehörigen Prozess wird geprüft ob dieser Zeitstempel
innerhalb eines gültigen Bereichs liegt. Auf diese Art kann eine Session
auf beispielsweise 30 Minuten begrenzt werden.
- Gültigkeitsdauer generell festlegen
Auch hier wird beim Erzeugen einer Session ein Timestamp in der DB abgelegt. Ein
per cron gesteuerter Prozess prüft regelmßig, ob es in der
Session-Datenbank Einträge gibt, die bestimmten Zeitkriterien nicht mehr
genügen - so können beispielsweise alle Einträge gelöscht
werden, die älter als ein Tag sind.
|
|
|