Verleiht Webseiten ein Gedächtnis: das Cookie
Desktop-Anwendungen wie Word oder Photoshop erinnern sich, welches Dokument zuletzt geöffnet war, wo welche Werkzeugleisten saßen und welche Schrift wir am liebsten benutzen.
Das HTTP-Protokoll hingegen ist zustandslos und hat kein Gedächtnis. Auch wenn wir zum zweiten, dritten Mal oder hundertsten Mal auf eine Webseite kommen, erinnert sich die HTML-Seite nicht an uns. Ohne Cookies müssten wir uns jedesmal und auf jeder Seite bei Pinterest und Twitter anmelden. Niemals würde sich der eigene Blog merken, dass wir angemeldet sind und lieber ohne grafischen Editor arbeiten.
const cookieName = "Theme"; const cookieValue = "Cantate"; Name, Schlüssel Wert │ │ ▼ ▼ document.cookie = cookieName + "=" + cookieValue + ";" + "max-age=604800000;" + "path=/;domain=meineseite.de"; ▲ ▲ │ │ Pfad ─┘ └─ Domainname zur Seite der Webseite
Cookies sind in RFC 2109 (HTTP State Management Mechanism) definiert, stammen aus der CGI-Programmierung und können neben CGI und Javascript auch von PHP gesetzt und gelesen werden.
document.cookie erzeugt, liest und löscht Cookies, die ein Gedächtnis für die Anwendung bilden: für das Nutzerverhalten, Warenkörbe und individuelle Einstellungen. Cookies speichern bis zu 4KB, und entsprechend der Spezifikation RFC 6265 (April 2011) soll ein Browser bis zu 3000 Cookies speichern.
Cookies sind berühmt, berüchtigt und in aller Munde, dabei sind sie ein Auslaufmodell: SessionStorage, LocalStorage und indexedDB sind die Nachfolger.
Cookie-Informationen
Cookies sehen aus wie Strings, sind aber Paare aus einem Schlüssel oder Namen und einem Wert, die im Browser des Besuchers gespeichert werden. Wenn wir die Seite später erneut besuchen, liest Javascript im Cookie nach, dass der Besucher sich eingeloggt oder angemeldet hat und zuletzt nach Waffeleisen, Kettensägen oder Javascript Cookies gesucht hat.
- comment (optional)
- Kann den Benutzer über die Nutzung des Cookies aufklären.
- domain (optional)
- Der Domainname der Webseite. Ein Cookie für die Subdomain blog.myhome.me gilt nur in der Subdomain. Der Name der Domain braucht mindestens zwei Punkte. Damit ein Cookie für alle Subdomainen gilt, beginnt der Name mit einem Punkt: domain=.myhome.me.
- HttpOnly
- gibt an, dass das Cookie nicht für Javascript zur Verfügung steht. HttpOnly wird in der HTTP Response gesetzt, also serverseitig. Wenn dynamische Werte erforderlich sind, leitet Javascript einen Request an den Server.
- max-age
- Die Geltungsdauer des Cookies (ersetzt das veraltete expires). Ohne die Geltungsdauer (in Sekunden, z.B. 60*60*24*3 – 3 Tage) läuft das Cookie ab, sobald der Besucher den Browser schließt.
- path
- Der Pfad zur Seite, die das Cookie setzt. Normalerweise gelten Cookies für Webseiten im aktuellen Verzeichnis und deren Unterverzeichnisse. Ein Cookie, das von myhome.me/archiv/page.html gesetzt wird, gilt also auch für myhome.me/archiv/andereseite.html und für myhome.me/archiv/daily/hierauch.html, aber nicht für myhome.me/tags/hiernicht.html. Wenn der Pfad nicht gesetzt wird oder path=/, kann das Cookie von jeder URL der Seite gelesen werden.
- secure
- Wenn der Wert den String secure enthält, kann das Cookie nur von einem sicheren Server (über HTTPS) gelesen werden. Bleibt das Flag leer, gibt es keine Einschränkung.
- version (dezimal, ganzzahlig)
- Version der Cookie-Spezifikation (version=1)
- expires
- Das alte Format des Ablaufdatums ist überflüssig, wird aber noch von allen Browsern unterstützt. Anstelle von expires tritt das einfachere max-age.
Third Party Cookies
Nur die Seite, die ein Cookie gesetzt hat, kann das Cookie auslesen.
Die Informationen, die ein Cookie mit sich bringt, bleiben selbst dem Benutzer der Seite verborgen. Per Voreinstellung der meisten Browser kann nur eine Seite, die wir besuchen, ein Cookie im Browser des Besuchers hinterlassen. Cookies von Dritten (Third Party Cookies) – typischerweise die Werbung auf der besuchten Seite oder der Versuch des Online Trackings durch Dritte – bekommen wir typischerweise durch iFrames, die per Javascript in die Webseite eingebettet werden.
In Chrome sehen wir die Cookies einer Seite in der Console (unter Weitere Tools / Entwicklertools / Application):
Cookie Blocker für Content Management Systeme wie WordPress, Drupal oder Joola gibt es in jeglicher Couleur. Außerhalb dieser Welt gibt es nur eine Methode, externe Scripte, Plugins, Module und Extensions vom Setzen von Cookies abzuhalten: Sie dürfen gar nicht erst geladen werden: Cookie-Blocker.
Session Cookies und Persistent Cookies
Wenn das Cookie kein Ablaufdatum (max-age oder expires) hat, wird es als Session Cookie bezeichnet. Session Cookies werden nicht auf die Festplatte des Benutzers übertragen. Sobald das Browserfenster geschlossen wird, ist das Cookie beim Besucher vergessen.
Session-Cookies können auch gesetzt werden, wenn Cookies im Browser des Besuchers deaktivert sind und werden gesetzt, wenn das Cookie am Ende der Sitzung gelöscht und vergessen werden soll. Das passiert z.B. beim Online-Banking, damit niemand nach dem Schließen des Browserfensters die Session wieder aufnehmen und Transaktionen durchführen kann.
In einem Session-Cookie sitzt meist nur eine Session-Kennung wie yuho23991uuyiao93, die möglichst lang und möglichst zufällig ist. Die Daten zur Session werden anhand der Session-ID auf dem Server gespeichert.
Persistent Cookies (langlebige Cookies) hingegen werden im Browser des Besuchers gespeichert, bis der Besucher sie manuell löscht oder ihre Zeit abgelaufen (expires) ist.
Cookies erzeugen
Javascript setzt das Cookie in die Eigenschaft cookie des document-Objekts als Zeichenkette. Cookies haben keine Eigenschaft und keine Methoden, also ist alles Handarbeit, wenn keine Library zugeschaltet wird.
document.cookie="theme=blue twenty; max-age=86400; path=/; domain=myhome.me"
- Nach einem Name-Wert-Paar steht ein Semikolon, evtl. gefolgt von einem Leerzeichen,
- gefolgt von der Geltungsdauer max-age, Semikolon, wieder evtl. gefolgt von einem Leerzeichen,
- am Ende stehen der Cookie-Pfad und die Cookie-Domaine.
Der Name des Cookies ist natürlich erforderlich und darf kein Semikolon, Gleichheitszeichen, Komma oder Blank (Leerzeichen) enthalten. Der Cookie-Wert hingegen ist optional. meincookie=; ist also korrekt.
Die Werte von Cookies dürfen kein Semikolon, kein Komma oder Leerzeichen enthalten. Darum codiert man den Wert vor dem Speichern im Cookie mit Javascript encodeURIComponent(). Bevor das Cookie gelesen werden kann, muss es mit decodeURIComponent() decodiert werden.
Cookies überschreiben
Javascript-Cookies mögen zwar aussehen wie Strings, sind aber keine Strings. Wird ein weiteres Cookie gesetzt:
document.cookie = 'nickname=Entchen; max-age=60*60*24*30 ; path=/";
dann überschreibt dieses Cookie das erste Cookie nicht. Wäre document.cookie ein String, wäre das erste Cookie überschrieben worden. Wird es aber nicht … Stattdessen sammeln sich Cookies im Cookie-String und beim Auslesen eines bestimmten Cookies muss der Name des Cookies ausgefiltert werden.
Nur ein zweites Cookie mit demselben Namen, Pfad und Domain würde das erste Cookie überschreiben. Unterscheiden sich hingegen Pfad oder Domain, dann existieren zwei Cookies mit demselben Namen. Das wird wohl selten so gewollt sein.
Cookies enabled?
Das Script testet mit Window Navigator, ob Cookies deaktiviert oder erlaubt sind und setzt dann ein Cookie als finalen Check, ob das gerade erzeugte Cookie existiert.
function cookiesEnabled () { let cookieEnabled = (navigator.cookieEnabled) ? true : false; if (typeof navigator.cookieEnabled == "undefined" && !cookieEnabled) { document.cookie="testcookie"; cookieEnabled = (document.cookie.indexOf("testcookie") != -1) ? true : false; } return (cookieEnabled); }
Und was ist, wenn der Benutzer Javascript deaktiviert hat?
Es gibt noch das noscript-Tag für alternatives Markup und Inhalt, das angezeigt wird, wenn der Benutzer Javascript deaktiviert hat.
Auch in PHP kann geprüft werden, ob Cookies erlaubt oder deaktivert sind.
<?php error_reporting (E_ALL ^ E_WARNING ^ E_NOTICE); // Wurde das Test-Cookie schon gesetzt? if ($_GET["set"] != "yes") { // Cookie setzen setcookie ("test", "test", time() + 60); // Seite neu laden header ("Location: cookies.php?set=yes"); } else { if (!empty($_COOKIE["test"])) { echo "Cookies sind in diesem Browser erlaubt"; } else { echo "Cookies sind in diesem Browser deaktiviert"; } }
Cookies ohne Javascript?
Cookies konnten zurvor auch mit einfachem HTML im einem Meta-Tag mit dem http-equiv-Attribut erzeugt werden.
<meta http-equiv="set-cookie" content="myCookie=Ich%20bin%20ein%20Cookie%20ohne%20Javascript">
Und PHP kann natürlich ebenfalls Cookies setzen:
setcookie(name, value, expire, path, domain, secure, httponly);
Cookies mit httponly können nicht mit Javascript gelesen werden, eine Sicherheitsvorkehrung gegen Cross Site Scripting (XSS). Google Chrome 65 hat den Support für http-equiv="set-cookie" bereits im März 2018 fallen lassen und Firefox will diesem Beispiel folgen.
Local Storage und Session Storage
Im Grunde genommen – so sagt der Standard – sollten Browser weder die Zahl der Cookies noch ihre Größe beschränken soweit es der Speicher des Browsers zulässt. Die Nutzlast von Cookies beträgt mindestens 4 KB und ein Browser soll mindestens 300 Cookies speichern, mindestens 20 Cookies per Host oder Domainname (HTTP State Management Mechanism). Wer sich die Cookies in seinem Browser vornimmt, stellt fest, dass sich schon Hunderte von Cookies unter dem Buchstaben "A" versammeln.
Das Auslesen von Cookies führt zu einem aufwändigen Transfer zwischen dem Browser und dem Server. Die eigentlichen Daten werden bei vielen Vorgängen ja nicht im Cookie selber gespeichert, sondern im Cookie liegt meist nur der Verweis auf Daten in einer Datenbank auf dem Server.
Da aber der Informationsaustausch zwischen dem Script und der Anwendung fundamental ist, setzt HTML5 auf ein neues Konzept: Local Storage und Session Storage ersetzen die altbackenen Cookies durch einen Speicher im Browser des Benutzers und beenden den unverschlüsselten Verkehr zwischen Browser und Server.