Hoch Inhalt dieses Kapitels:

Linkwerk
screeneXa

Erhältlich u.a. bei  Amazon.de

8

Cookies

Von Wolfgang Schwarz

8.1 Grundlagen

Cookies (Kekse) sind kleine Informationshäppchen, die eine Webseite auf dem Rechner des Anwenders ablegen und später wieder auslesen kann. In diesem Kapitel erfahren Sie, wie man Cookies setzt, liest und ihren Verfallszeitpunkt festlegt. Nebenbei lernen Sie noch etwas über das nützliche Date-Objekt.

Cookie-Paranoia

Die Geschichte der HTTP Cookies gehört in das große Buch der unerklärlichen psychologischen Massenphänomene, von denen die Menschheit hin und wieder heimgesucht wird. Seit ihrer Einführung durch Netscape 2 ranken sich die bizarrsten Gerüchte um die kleinen Kekse: angefangen vom Ausspionieren privater Internetgewohnheiten bis zu Angriffen auf die Windows-Registry soll mit Cookies angeblich alles möglich sein.

In Wirklichkeit kann man, von gelegentlichen Browserbugs einmal abgesehen, aus Cookies nur lesen, was man selbst vorher hineingeschrieben hat, und hineinschreiben kann man nur kleine Textstücke von maximal 4 KB Größe (das sind 4000 Zeichen). Viele Online-Shops verwenden Cookies als Einkaufskorb, in dem sie festhalten, welche Artikel bereits ausgewählt wurden. Andere Seiten legen in Cookies Benutzereinstellungen ab, damit diese nicht bei jedem Besuch neu festgelegt werden müssen.

Cookies lesen und schreiben

document.cookie

Mit JavaScript finden wir die Cookies einer Seite in der Eigenschaft cookie des Objekts document:

document.cookie

Wenn Sie diesen Wert auslesen, erhalten Sie einen String, in dem alle für die Seite zugänglichen Cookies, durch Semikolons getrennt, als Name=Wert-Paare abgelegt sind. Ich habe zum Beispiel eben auf www.yahoo.com die Anweisung

javascript:alert(document.cookie)

in die Location-Zeile meines Browsers geschrieben und diese Ausgabe bekommen:

B=dmu8veh9mph4t

Interessant, nicht wahr?

Pro Domain dürfen bis zu 20 Cookies gesetzt werden. Um einen Cookie zu setzen, weist man dem cookie-Objekt einfach ein neues Name=Wert-Paar zu. Wollen Sie also beispielsweise die Information speichern, dass B derzeit dmu8veh9mph4t ist, dann schreiben Sie:

document.cookie = "B=dmu8veh9mph4t";

Wie Sie sehen, ist Lesen und Schreiben bei Cookies nicht symmetrisch: Schreiben können Sie immer nur einzelne Name=Wert-Paare, beim Lesen erhalten Sie aber alle Paare, die im Moment für Ihre Seite gelten. Das sind gewöhnlich alle diejenigen, die die Seite selbst oder eine in der Verzeichnishierarchie darüber liegende Seite gesetzt hat.

Cookie-Parameter

Wenn Sie einen Cookie schreiben, können Sie festlegen, für welche Seiten er gelten soll, das heißt, von wo aus auf ihn zugegriffen werden darf. Dazu gibt es die Parameter path und domain:

document.cookie =
   "Name=Wert; path=Verzeichnis; domain=Domain"

Lassen Sie diese Angaben weg, wird automatisch als path das aktuelle Verzeichnis und als domain der Hostname Ihres Servers eingetragen.

Beispiel

Ein Beispiel: Angenommen, Sie sind Webmaster von www.greenpeace.de und setzen auf der Seite www.greenpeace.de/start.html einen Cookie mit dem Inhalt "Lieblingszahl=249.95". Später schreiben Sie auf www.greenpeace.de/hier/dort/spaeter.html noch einen, sagen wir: "Lieblingssenf=Dijon". Jetzt liefert document.cookie auf der Seite spaeter.html:

"Lieblingszahl=249.95; Lieblingssenf=Dijon"

auf start.html aber nur:

"Lieblingszahl=249.95"

Um den zweiten Cookie auch auf start.html lesbar zu machen, müssen Sie beim Schreiben die path-Angabe benutzen:

document.cookie = "Lieblingssenf=Dijon; path=/"

Damit der Cookie auch von Ihren anderen Servern aus gelesen werden kann, zum Beispiel von www2.greenpeace.de, verwenden Sie domain:

document.cookie =
   "Lieblingssenf=Dijon; path=/; domain=greenpeace.de"

Aus Sicherheitsgründen können Sie übrigens nur Ihre eigene Domain angeben.

Neben path und domain gibt es noch zwei weitere Attribute, die Sie beim Schreiben eines Cookies festlegen können (lesen kann man diese Parameter übrigens nicht): expires und secure. Die Angabe von secure sorgt dafür, dass der Cookie nur über sichere Verbindungen (SSL) gelesen werden darf. Der expires-Wert bestimmt den Verfallszeitpunkt des Cookies. Er sollte im Format GMT (Greenwich Mean Time) bzw. UTC (Universal Time Coordinated, der neue Name für GMT) stehen. Eine solche Zeitangabe sieht zum Beispiel so aus:

   Tue, 07 Nov 2084 12:00:00 GMT

Ohne eine expires-Angabe wird der Cookie beim nächsten Schließen des Browsers gelöscht.

Sie können einen Cookie sofort löschen, indem Sie ihm ein in der Vergangenheit liegendes Verfallsdatum geben.

Zeiten in JavaScript: Das Date-Objekt

Für den Umgang mit Zeiten stellt JavaScript einen eigenen Objekttyp zur Verfügung: Date. Eine Instanz davon erzeugen Sie mit

ein Datum erzeugen

var datum = new Date();

Jede Date-Instanz repräsentiert einen bestimmten Zeitpunkt. Wenn Sie wie im obigen Beispiel keine Parameter übergeben, steht das Objekt für die Zeit, an der es erzeugt wurde. Um einen anderen Zeitpunkt darzustellen, übergeben Sie einfach das entsprechende Datum an den Konstruktor. Sie können es entweder als Zahl der Millisekunden seit dem 1. Januar 1970, 00:00 Uhr GMT angeben:

var datum = new Date(Millisekunden)

oder, wenn Sie diesen Wert gerade nicht parat haben, in etwas gewohnterer Form:

var datum = new Date(Jahr, Monat, Tag, Stunden, Minuten,
                     Sekunden, Millisekunden)

Alle diese Werte sind vom Typ Integer. Die Angabe von Stunden, Minuten, Sekunden und Millisekunden ist optional.

Hinweis

Beachten Sie, dass die Zählung der Monate bei 0 beginnt. 0 steht also für Januar, 11 für Dezember.

Wir erhalten deshalb mit

var mauerfall = new Date(1989, 10, 9)

ein Objekt, das den 9. 11. 1989 repräsentiert.

Methoden von Date

Das Date-Objekt verfügt über einige recht praktische Methoden. So liefert

mauerfall.getDate()

den Tag des mauerfall-Datums, also 9. Die anderen Werte bekommen Sie mit getMilliseconds(), getSeconds(), getMinutes(), getHours(), getMonth() (beim mauerfall also 10), getYear() und getDay(). Letzteres steht für den Wochentag (auf Englisch), also zum Beispiel "Tuesday".

Die Implementation von getYear() ist leider bei den Browserherstellern etwas verunglückt: Internet Explorer 3, Netscape 4 und Netscape 6 liefern hier die Jahreszahl gerechnet ab 1900, also beispielsweise den Wert 102 für das Jahr 2002. Navigator 2 und 3 und Internet Explorer 4 und 5 geben dagegen für Jahre ab 2000 die vierstellige Jahreszahl zurück. Werte vor 1970 können ältere Browser überhaupt nicht darstellen. Seit JavaScript1.3 gibt es getFullYear(), was in jedem Fall die vollständige Jahreszahl zurückgeben sollte.

Für die Arbeit mit Cookies sind vor allem die Methoden getTime(), setTime() und toGMTString() interessant. getTime() liefert die verstrichenen Millisekunden zwischen dem 1. Januar 1970 und dem repräsentierten Datum. Mit setTime() können Sie ein Date-Objekt im Nachhinein auf eine andere Zeit stellen. toGMTString() (seit JavaScript1.3 auch toUTCString()) schließlich gibt die Zeit im GMT- bzw. UTC-Format zurück. (Sollte es jedenfalls. Die MacIntosh-Versionen von Netscape verrechnen sich dabei jedoch um einen Tag: Sie sind immer 24 Stunden voraus.)

In den Übungen werden Sie sehen, wie man die Date-Methoden für die Arbeit mit Cookies einsetzt.

Alternativen zu Cookies

Die Beschränkungen von Cookies liegen auf der Hand: Sie können nur relativ wenig Informationen in einem recht unflexiblen Format speichern. Wegen der teilweise fehlerhaften Umsetzung in den Browsern sollten außerdem empfindliche Daten am besten gar nicht in Cookies abgelegt werden. Dazu kommt, dass viele Leute aus Angst vor der Cookie-Attacke die entsprechenden Funktionen ihres Browsers kurzerhand abgeschaltet haben.

Die sicherste Alternative zu Cookies ist die Daten serverseitig mit CGI zu speichern.

Wenn Sie nur Informationen von einer Seite auf die nächste übermitteln wollen, können Sie sie auch, wie in Kapitel 7 beschrieben, als search-Parameter an den URL der aufgerufenen Seite hängen ( zum Beispiel:

location.href="naechsteSeite.html?B=dmu8veh9mph4t";

Auf naechsteSeite.html finden Sie dann unter

window.location.search

den übergebenen String "?B=dmu8veh9mph4t".

Oder Sie legen die Daten im name-Attribut des window fest. Hier bleiben sie so lange bestehen, bis der Name explizit geändert oder das Fenster geschlossen wird.

Internet Explorer bietet außerdem seit Version 5 spezielle Persistenz-Techniken, die auf DHTML-Behaviors beruhen, auf die wir aber hier nicht eingehen können.

8.2 Übungen

Uebung

leicht

1. Übung

Erstellen Sie eine Seite mit zwei Links. Wenn man auf den ersten klickt, soll ein Cookie mit dem Inhalt "Nachtisch=Kekse" gesetzt werden. Ein Klick auf den zweiten Link soll den Cookie-Inhalt in einem alert-Dialog ausgeben. Der Cookie muss nicht für längere Zeit gespeichert werden.

Uebung

leicht

2. Übung

Wenn Sie zwei Cookies mit demselben Namen anlegen, überschreibt der zweite den ersten. Löschen Sie den Cookie von Übung 1, indem Sie ihn nochmal setzen, diesmal aber mit einem vergangenen expires-Attribut.

Uebung

mittel

3. Übung

Schreiben Sie eine Funktion, die überprüft, ob der Besucher Ihrer Seite Cookies aktiviert hat.

Uebung

mittel

4. Übung

Einige Browser erlauben die (recht unbequeme) Option, dass jeder Versuch, einen Cookie zu setzen, eigens bestätigt werden muss. Ändern Sie die Cookietest-Funktion von Übung 3, so dass sie einen von drei Werten zurückliefert: 0 wenn Cookies deaktiviert sind, 1 wenn sie aktiviert sind und nicht bestätigt werden müssen, 2 wenn jedesmal eine Bestätigung nötig ist.

Uebung

mittel

5. Übung

Wie Sie wissen, liefert document.cookie oft nicht nur einen Cookie, sondern eine Liste aller Cookies, die für die Seite zugänglich sind. Schreiben Sie eine allgemein verwendbare Funktion liesCookie(), die diese Liste nach einem bestimmten Cookie durchsucht. Der Name des Cookies wird als Argument übergeben, die Funktion liefert dann den dazugehörigen Wert zurück.

Uebung

mittel

6. Übung

Als Nächstes schreiben Sie eine allgemein verwendbare Funktion zum Setzen eines Cookies. Dieser Funktion werden bis zu sechs Parameter übergeben: Name, Wert, Verfallsdatum, Pfad, Domain und secure-Einstellung.

Versuchen Sie auch, den MacIntosh-Bug des Netscape Navigators zu berücksichtigen: Legen Sie dazu beispielsweise mit

new Date(1970,0,1,12);

ein Date-Objekt an, das den Mittag des 1. 1. 1970 repräsentiert. Bei Netscape auf MacIntosh enthält dieses Datum, ins GMT-Format übertragen, eine "02". Fragen Sie das ab und korrigieren Sie entsprechend das Datum.

Uebung

leicht

7. Übung

Auf einer Internetseite sollen die Besucher persönlich begrüßt werden. Dazu werden sie, wenn Cookies aktiviert sind, beim ersten Besuch nach ihrem Namen gefragt. Dieser wird in einem Cookie gespeichert und bei jedem Betreten der Seite ausgelesen und angezeigt.

Erstellen Sie diese Seite. Verwenden Sie dabei die Funktionen cookieAktiviert(), liesCookie() und schreibCookie() aus den Übungen 3, 6 und 7.

Uebung

schwer

8. Übung

Zum Schluss noch ein sinnvolles Anwendungsbeispiel.

Schreiben Sie eine Seite, die den Besucher über alle Änderungen informiert, die sich dort seit seinem letzten Besuch zugetragen haben. Das Datum des letzten Besuchs halten Sie in einem Cookie fest ( aber nur, wenn der Besucher es wünscht.

Das bedeutet: Ist noch kein Cookie gesetzt, wird lediglich auf die Möglichkeit hingewiesen. Über einen Link kann der Besucher dann selbst den Cookie anlegen, wenn er möchte.

8.3 Tipps

Tipp zu 3:

Tipps zu 4:

Tipps zu 5:

Tipps zu 8:

8.4 Lösungen

Lösung zu 1:

<html>
<head>
<title></title>
</head>
<body>
<a href="#" onClick="document.cookie='Nachtisch=Kekse'">
setzen</a><br>
<a href="#" onClick="alert(document.cookie)">lesen</a>
</body>
</html>

Lösung zu 2:

Wir erzeugen als erstes irgendein Datum in der Vergangenheit:

var einst = new Date(1984, 0, 1);

formatieren es als GMT-String:

var einstGMT = einst.toGMTString();

und hängen es als expires-Wert an den Cookie:

document.cookie = "Nachtisch=irgendwas; expires="+einstGMT;

Lösung zu 3:

function cookieAktiviert() {
   // Test-Cookie setzen
   document.cookie = "testCookie=bla";
   // Cookie lesen:
   var keks = document.cookie

   // Wenn keks nicht undefiniert ist und "testCookie"
   // enthält, wurde der Cookie erfolgreich gesetzt:
   if (keks && keks.indexOf("testCookie") > -1) return true;

   // andernfalls werden Cookies nicht unterstützt:
   else return false;
}
// ein Test:
alert(cookieAktiviert());

Ab Version 4 des Internet Explorers und Version 6 von Netscape steht mit navigator.cookieEnabled auch eine vordefinierte Eigenschaft zur Verfügung, die angibt, ob Cookies aktiviert sind.

Lösung zu 4:

function cookieAktiviert() {
   // Start-Zeitpunkt festhalten
   var start = new Date();

   // Test-Cookie setzen
   document.cookie = "testCookie=test";

   // Die vergangene Zeit messen
   var jetzt = new Date();
   var dauer = jetzt.getTime()-start.getTime();

   // Wenn mehr als 500 ms vergangen sind, wurde ein Dialog
   // eingeblendet
   if (dauer > 500) {
      return 2;
   }
   else {
      // Cookie lesen
      var keks = document.cookie
      if (keks && keks.indexOf("testCookie") > -1) return 1;
      else return 0;
   }
}

Lösung zu 5:

function liesCookie(name) {
   var keks = document.cookie;

   // Anfangsposition des Name=Wert-Paars suchen
   var posName = keks.indexOf("; " + name + "=");
   if (posName == -1) {
      // vielleicht war's der erste Name in der Liste?
      if (keks.indexOf(name + "=") == 0) posName = 0;
      // nein? dann abbrechen mit Rückgabewert null
      else return null;
   }

   // Anfangs- und Endposition des Krümelwerts suchen
   var wertAnfang = keks.indexOf("=", posName)+1;
   var wertEnde = keks.indexOf(";", posName+1);
   if (wertEnde == -1) wertEnde = keks.length;

   // Krümelwert auslesen und zurückgeben
   var wert = keks.substring(wertAnfang, wertEnde);
   return unescape(wert);
}

Lösung zu 6:

// Diese Funktion korrigiert den Datums-Bug von
// Netscape/Mac und liefert den korrekten GMTString:
function fixedGMTString(datum){
   var damals=new Date(1970,0,1,12);
   if (damals.toGMTString().indexOf("02")>0) {
      datum.setTime(datum.getTime()-1000*60*60*24);
   }
   return datum.toGMTString();
}
function schreibCookie(name,wert,verfall,pfad,dom,secure) {
   neuerKeks = name + "=" + escape(wert);
   if (verfall)
      neuerKeks += "; expires=" + fixedGMTString(verfall);
   if (pfad) neuerKeks += "; path=" + path;
   if (dom) neuerKeks += "; domain=" + dom;
   if (secure) neuerKeks += "; secure";
   document.cookie = neuerKeks;
}

Lösung zu 7:

Für die Ein- und Ausgabe des Besuchernamens gibt es verschiedene Wege. Wir wählen, besonders unelegant, prompt zur Eingabe und alert zur Ausgabe.

...
<script language="JavaScript">
<!--
function cookieAktiviert() {
   // siehe Übung 3
}

function liesCookie(name) {
   // siehe Übung 5
}

function schreibCookie(name,wert,verfall,pfad,dom,secure) {
   // siehe Übung 6
}

if (cookieAktiviert()) {
   var besucherName = liesCookie("Besuchername");
   if (besucherName == null) {
      besucherName = prompt("Guten Tag. Wie ist Ihr Name?");
      // Verfallszeitpunkt nach 365 Tagen bestimmen
      var jetzt = new Date();
      var verfall =
         new Date(jetzt.getTime() + 1000*60*60*24*365);
      schreibCookie("Besuchername", besucherName, verfall)
   }
   alert("Hallo " + besucherName + "!");
}
// -->
</script>
...

Überlegen Sie sich gut, ob Sie so etwas wirklich verwenden wollen. Erfahrungsgemäß sind Menschen nicht besonders glücklich darüber, wildfremden Seiten erst ihren Namen sagen zu müssen, bevor sie etwas zu sehen bekommen. Ich trage zum Beispiel in solchen Fällen nur schnell irgendwelche Buchstaben ein ( bei jedem weiteren Besuch werde ich dann mit "Hallo asdfjk!" begrüßt.

Lösung zu 8:

Dieses Skript muss im body der Seite stehen, weil es document.write verwendet, um dynamisch ausgewählten Text zu schreiben.

// Der Array mit den Änderungen
aenderungen = new Array(
   new Date(2000,07,10), "- Hintergrundfarbe jetzt rot",
   new Date(2000,09,15), "- neue Bilder von der Katze",
   new Date(2001,00,05), "- Hintergrundfarbe wieder grün"
);

function liesCookie(name) {
   // siehe Übung 6
}

function schreibCookie(name,wert,verfall,pfad,dom,secure) {
   // siehe Übung 7
}

// Cookie lesen
var warda = liesCookie("letzterBesuch");

if (warda) {
   // Wenn der Cookie da ist, das entsprechende Date-Objekt
   // erzeugen und die Liste der Änderungen seit diesem
   // Datum schreiben:
   wardaDatum = new Date(warda*1);
   var str = '';
   for (i=0; i<aenderungen.length; i+=2) {
      if (wardaDatum.getTime()<aenderungen[i].getTime())
         str+= aenderungen[i+1]+'<br>';
   }
   if (str=='')
      str = 'Keine Änderungen seit Ihrem letzten Besuch';
   else str = 'Seit Ihrem letzten Besuch am '
      + wardaDatum.getDate()+'.'
      + (wardaDatum.getMonth()+1)+'. '
      + 'gab es hier diese Änderungen:<br>'
      + str;
   document.write(str);
   // Den Cookie erneuern
   cookieSetzen();
}

else {
   // Wenn kein Cookie gesetzt ist, auf die Möglichkeit
   // hinweisen
   var str = 'Sie können in Zukunft an dieser Stelle über '
      + 'alle Änderungen seit Ihrem letzten Besuch '
      + 'informiert werden. Dazu wird ein Cookie gesetzt, '
      + 'der das heutige Datum speichert. Wenn Sie das '
      + 'wünschen, klicken Sie auf diesen Link:<br>'
      + '<a href="javascript:cookieSetzen()">'
      + 'Cookie setzen</a>';
   document.write(str);
}

function cookieSetzen() {
   var jetzt = new Date();
   schreibCookie("letzterBesuch", jetzt.getTime(),
      new Date(jetzt.getTime()+1000*60*60*24*100));
}

Linkwerk
screeneXa