C is for Cookie

Krümelmonster: C is for Cookie
Auf den vielfachen Wunsch einer einzelnen Dame hier der Erklärbar zum Thema Kekse für die Freunde besagter Dame.
Eines der Hauptprobleme, das man lösen muß, wenn man Webanwendungen schreibt, ist die Tatsache, daß man im wesentlichen Memento dreht.
Jedesmal, wenn man im Browser eine Seite aufruft, sieht der Webserver einen Request, bearbeitet diesen und vergißt danach, was er getan hat. Es gibt nichts an einem Request, das man verwenden kann, um den zweiten Requests sicher mit dem ersten Request zu verketten und sich an die Dinge zu erinnern, die man im ersten Request getan hat.
Cookies und Sessions
GET /
an einen Server www.shop.com
(links). Der erste und der zweite Request sind unabhängig und nicht miteinander verkettbar.
Der erste und der zweite Request vom selben Browser sind nach Definition von HTTP für den Webserver nicht als zusammengehörig erkennbar.
Man kann nicht den User-Agent des Browsers verwenden, weil es viele verschiedene Browser mit demselben User-Agent geben kann. Man kann auch nicht die IP-Adresse verwenden: zum einen kann es vorkommen, daß mehr als ein Benutzer dieselbe IP-Adresse verwendet, weil es Rechner gibt, die von mehreren Benutzern zeitgleich verwendet werden.
Zum anderen kann es vorkommen, daß Request 1 und Request 2 mit unterschiedlichen IP-Adresse gesendet werden.
Das ist oft der Fall, wenn ein Benutzer bei T-Online oder AOL ist oder wenn er Tor verwendet. Dort geschieht dies regelmäßig wegen der Struktur der Proxyserver, die dort verwendet werden.
Bei Mobilbenutzern kann sich die IP-Adresse spontan ändern, wenn sie ihren Standort verändern, auch wenn das Netz versucht, dies zu verbergen.
Und nach einer DSL-Zwangstrennung hat man natürlich auch eine neue IP-Adresse. Erstrecken sich Browser-Tätigkeiten über solche Intervalle, sieht man aufeinanderfolgende Requests desselben Benutzers von verschiedenen IP-Adressen.
Bei Memento löst man das Problem mit Tätowierungen, in Browsern verwendet man stattdessen Cookies.
Set-Cookie
einen Cookie im Browser, hier sessid=17
.
Greift ein Browser das erste Mal auf eine Webanwendung zu, sendet er einen GET
-Request mit dem Namen der gewünschten Site im Host
-Header und einigen weiteren Headerzeilen, die uns hier heute nicht interessieren sollen.
GET / HTTP/1.1
Host: www.shop.com
Connection: keep-alive
Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.84 Safari/534.13
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Die Webanwendung reagiert mit einer Antwort, die einen Content-Type definiert (zum Beispiel text/html
für Webseiten) und die weitere Header enthalten kann.
Einer dieser Header ist Set-Cookie
.
Das ist die Tätowierung, die unseren Browser von allen anderen Browsern unterscheidbar macht.
Set-Cookie: <name>=<value>
[; expires=<date>]
[; domain=<domain_name>]
[; path=<some_path>]
[; secure]
[; httponly]
Die “Syntaxdefinition” von Set-Cookie
beschreibt, welche Eigenschaften und Optionen ein Cookie haben kann.
Set-Cookie
definiert eine Variable mit dem Namen Name
und dem Wert value
.
Es gibt Limits im Browser für die Anzahl und Länge dieser Variablen
pro Site und insgesamt.
Mit der Definition eines Cookies können weitere Einschränkungen festgelegt werden, die sagen wo und wie lange der Cookie gelten soll: Die Einschränkungen, die man festlegen kann, bestimmen an welche Domain der Cookie zurück übermittelt wird, wie lange er gelten soll, ob er für eine ganze Site oder nur einige Pfade auf der Site definiert sein soll, ob er nur mit SSL-Verschlüsselung übertragen werden soll und ob Javascript mit dem Cookie arbeiten darf.
Der Browser erhält diese Definition mit der Webseite, die ihm übermittelt wird. Während er die Webseite darstellt, speichert er die Variable und den dazu gehörenden Wert ab.
Im Beispiel oben wird eine Variable mit dem Namen sessid
definiert, die den Wert 17
bekommt.
Es ist eine zusätzliche Einschränkung definiert, die festlegt, daß diese Variable bei allen Zugriffen auf Domains mit der Endung .shop.com
übermittelt werden soll.
Da kein expires
-Wert gesetzt ist, gilt die Variable so lange wie der Browser läuft - andernfalls gälte sie so lange bis das gesetzte Datum verstrichen ist.
Mit jedem weiteren Zugriff (der die zusätzlichen Einschränkungen erfüllt)
setzt er jetzt eine zusätzliche Headerzeile Cookie
, in der er die Variable
und ihren Wert zurück an den Server übermittelt.
Set-Cookie
ist jetzt ein Cookie sessid
mit dem Wert 17
für .shop.com
gesetzt. Weil www.shop.com
unterhalb von .shop.com
liegt, wird nun bis auf weiteres der Cookie: sessid=17
mit jedem Request dort hin gesendet. Aber nur für diesen Browser - andere Browser senden keinen Cookie oder den Wert, der für sie gesetzt worden ist. Dadurch werden unterschiedliche Browser unterscheidbar.
Sobald ein Cookie gesetzt ist, übermittelt der Browser den Cookie bei jedem nachfolgenden Requests, der die Bedingungen der Definition erfüllt.
Hier wird also nun für jeden Request an einen Host in der Domain .shop.com
die
sessid=17
mitgesendet.
Der Browser ist nun eindeutig identifizierbar, solange die sessid
eindeutig vergeben wird.
Unser Beispielcookie ist ein sogenannter Session-Cookie - er wird ohne ein expires
definiert und daher nicht auf der Festplatte, sondern nur im Browser gespeichert.
Beendet man den Browser, ist er verloren.
Session-Cookies dienen dazu, aufeinanderfolgende Web-Requests ein und desselben Browsers miteinander zu verketten und erlauben es Webanwendungen, Zustand zu haben.
Zustand?
Eine Anwendung ist ein Programm, das bestimmte Funktionen hat. Entgegen den Darstellungen in gewissen Filmen liegen Programme gewöhnlich auf der Platte rum und tun gar nichts. Nur wenn sie gestartet werden, wird ihr Code um etwas ergänzt - Variablen. Die Summe aller Variablen eines Programmes ist sein Zustand.
Ein Texteditor zum Beispiel hat viele Funktionen zum Bearbeiten von Texten.
Öffnet man ein neues Dokument, weiß der Textedit den Namen dieses Dokumentes (“untitled.txt”), die Cursorposition (links oben) und den Text, der in dem Dokument enthalten ist sowie die aktuelle Schriftart und -farbe und noch viele andere Dinge mehr.
Ein zweites Dokument (hier: “Faust 3 - die Rückkehr.txt”) enthält anderen Text, eine andere Cursorposition, eine andere Schriftart und so weiter. Der Texteditor kann mehr als einen Text bearbeiten, weil er für jedes Dokument seinen Zustand getrennt verwaltet.
sessid=17
, der andere die sessid=18
. In zwei unterschiedlichen Datensätzen wird der Warenkorb für jeden Browser verwaltet.
Genau so verhält es sich etwa bei einem Webshop. Auf dem Server liegt einmal der Code der Webanwendung, mit Funktionen wie “Artikel anzeigen”, “Warenkorb verwalten” und “abkassieren”. Aber natürlich muß man für jeden Benutzer des Webservers - womöglich viele hundert Personen gleichzeitig - einen eigenen Warenkorb verwalten.
Man kann den Warenkorb selbst in den Cookie tun, aber das hat eine Reihe von Nachteilen. Zum Beispiel ist die Größe und Lebensdauer des Warenkorbes dann von den Limits des Browsers eingeschränkt. Zum anderen ist es dann so, daß die Daten des Warenkorbes bei jedem Request mit übermittelt werden müssen. Das macht jeden Request größer. Außerdem es macht auch bei jedem Request einen Übergang von Daten aus der Gefahrenzone “nicht vertrauenswürdig” (irgendein Browser im Internet) in die Gefahrenzone “vertrauenswürdig” (unsere Webanwendung) notwendig, und führt so zu vielen unnötigen Prüfungen der Datenintegrität.
Normalerweise setzt man in einem Session-Cookie nur eine eindeutige Kennummer, die Session-ID. Die Daten des Warenkorbes selber speichert man in einem Datensatz auf dem Server in einem Eintrag mit genau dieser Kennung.
Bekommen wir also einen Request von einem Browser mit der sessid=17
, laden
wir den Datensatz für die sessid 17
und zeigen dem Kunden seinen Warenkorb
an.
Bekommen wir stattdessen einen Request für die sessid 18
, laden wir diesen Warenkorb und zeigen diesen an.
Natürlich wird man nicht wirklich die Zahlen 17
und 18
verwenden, sondern sehr große, zufällig gewählte Zahlen wie etwa a13bcf7a0b2fb6f9dfdebe2e0657c9e3
und 791c8d7234c56497fe23a593af3b1ab9
.
Sähe ein Hacker etwa eine sessid wie 17
, würde er sofort einmal nachsehen, ob es 16
und 18
gibt und welche Daten da drin stecken.
Bei a13bcf7a0b2fb6f9dfdebe2e0657c9e3
sind nicht nur andere IDs schwer zu raten, sondern der gesamte Session-ID Raum ist dünn besetzt, d.h. die meisten anderen geratenen IDs gehören zu gar keiner Session.
Was wäre, wenn es keine Cookies gäbe?
Man könnte Session-IDs auch anders als mit Cookies übermitteln. Statt des Requests
GET /shop.html
Host: www.shop.com
Cookie: sessid=17
könnte man zum Beispiel auch die ID in die URL tun.
Der Request wäre dann zu http://www.shop.com/shop.html?sessid=17
:
GET /shop.html?sessid=17
Host: www.shop.com
Oder man baut die ID in den Hostnamen ein:
http://sessid17.shop.com/shop.html
wäre die URL und der Request ist dann
GET /shop.html
Host: sessid17.shop.com
Das Problem bei diesen anderen Verfahren ist, daß die Session-ID auf die eine oder andere Weise Bestandteil der URL wird.
Dadurch gibt man die Session weiter, wenn man die URL an jemand anders per Cut & Paste per Mail, Skype oder sonstwie übermittelt. Ja, die URL wird sogar unter Umständen mit der Session-ID von Suchmaschinen archiviert - eine ganz schlechte Idee, denn so bekommt jeder Benutzer der Suchmaschine dieselbe Session-ID, wenn er über die Suchmaschine in unseren Shop kommt.
Es gibt Mechanismen, die mit diesen Problemen fertig werden und die eine URL auch dann sicher machen können, wenn sie die Session-ID in der URL enthält. Es ist aber viel besser, einen Mechanismus zu verwenden, der das Problem schon im Ansatz vermeidet - und das ist genau die Intention, die hinter der Erfindung von Cookies stand.
Cookies mit Datum und Auto-Login
Eine weitere Anwendungsmöglichkeit von Cookies ist die Speicherung von Präferenzen im Webbrowser, sodaß eine Website bei folgenden Besuchen gleich so aussieht, wie vom Benutzer voreingestellt.
Dazu wird ein Cookie mit einem Expires-Wert gesendet, der dann womöglich erst sehr weit in der Zukunft seinen Inhalt verliert. Dadurch wird der Cookie nicht mehr im Browser gespeichert, sondern auf der Festplatte in der Browserkonfiguration verewigt. Er steht auch nach einem Neustart noch zur Verfügung.
In dem Wert des Cookies werden die Benutzer-Voreinstellungen gespeichert. Oder gar ein Login-Token, das den Benutzer eindeutig identifiziert, und das auf diese Weisen den Benutzer bei allen weiteren Besuchen auf der Site einloggt. So ein Token muß natürlich schwer zu manipulieren sein, aber wie man so etwas bauen kann ist gut verstanden.
Ein Webserver kann so ein Cookie etwa wie folgt setzen:
Set-Cookie: Login=kris-23b9404f78d90b881175bcfc5f57b61c;
domain=.shop.com;
expires=Sat, 01-Jan-2030 00:00:00 GMT;
path=/;
Die Site hat nun Methoden, mit denen sie die behauptete Identität (kris
) und den Authentizitätsbeweis (die Nummer da) zusammenführen kann.
Sie kann dann ableiten, daß kris
wirklich mal irgendwann in diesen Browser sein Paßwort getippt hat.
Sie loggt kris
nun automatisch ein - für die nächsten 20 Jahre.
Die dunkle Seite der Kekse - Präferenzen und Tracking
Gesetzte Cookies werden bei jedem Request übermittelt, der die im Set-Cookie
gestellten Bedingungen erfüllt.
Hat man also eine Webseite, die Bilder enthält, wird der Cookie auch bei allen Requests für Bilddateien mitgesendet.
Der Cookie-Mechanismus hat Einschränkungen, etwa bei der Domain-Bedingung.
So kann man als www.shop.com
zwar einen Cookie für domain=.shop.com
setzen (also auch für img.shop.com
, static.shop.com
und so weiter), aber nicht für .com
oder gar .
(alle Sites im Internet).
Dieser etwas unbeholfene Mechanismus soll verhindern, daß man Cookies als Tracking-Mechanismus mißbrauchen kann.
Denn wäre es legal, einen Cookie wie diesen für die “Domain” .
(das ganze
Internet) zu senden:
Set-Cookie: ID=ee487f1423d28992ee285760875e2cb8;
expires=Sat, 01-Jan-2030 00:00:00 GMT;
domain=.;
dann hätte man dem Browser des Benutzers für die nächsten beiden Dekaden internet-weit eine eindeutige Kennung verpaßt und könnte diesen Benutzer bequem tracken.
Leider ist die Regel “zwei Punkte im Domainnamen” für viele Domains kaputt.
Ein Beispiel: www.shop.co.uk
- mit der Zwei-Punkte-Regel kann man Cookies für ganz .co.uk
setzen.
Deswegen haben Browser in der Regel eine sehr lange und komplizierte Liste von Ausnahmefällen, die versucht, diesen defekten Cookie-Sicherheitsmechanismus zu reparieren.
Tracking mit Cookies - und mit Javascript am Beispiel IVW
Werbebanner-Generierer und Auflagenzähler umgehen das Problem sowieso auf anderen Wegen. In den Seiten von web.de und bei vielen anderen Websites findet man zum Beispiel Code wie diesen hier:
<img src="http://webdessl.ivwbox.de/cgi-bin/ivw/CP/264_PH;sc%3Dwebde/homepage"
alt=""
width="1" height="1"/>
Dieser Code lädt ein 1x1 Pixel großes Bild - das Bild ist ein transparentes GIF, also unsichtbar.
Das Bild wird aber nicht von der Domain web.de
geladen, sondern von der Domain ivwbox.de
.
Von dort bekommt man dann eine Antwort wie diese hier:
KK:~ kris$ curl -D x 'http://webdessl.ivwbox.de/cgi-bin/ivw/CP/264_PH;sc%3Dwebde/homepage'
KK:~ kris$ cat x
HTTP/1.0 302 FOUND
Date: Sat, 05 Feb 2011 18:20:49 GMT
...
Set-Cookie: i00=01914d4d94fc5da00006; path=/; domain=.ivwbox.de; expires=Sunday, 05-Feb-2012 18:20:44 GMT
Es wird also der Cookie i00
mit dem Wert 01914d4d94fc5da00006
definiert.
Der Cookie hält ein Jahr lang und wird für die Domain .ivwbox.de
gesetzt.
Diese Domain ist von der HTML-Webseiten-Domain web.de
verschieden.
Man spricht von einem Third-Party Cookie.
Immer wenn ich Seiten von web.de
lade, wird das unsichtbare Zählpixel von webdessl.ivwbox.de
mit geladen und ich werde als Benutzer 01914d4d94fc5da00006
identifiziert.
Die IVW
kann so die eindeutig unterscheidbaren Benutzer auf web.de
zählen und so eine Werbe-Auflagenstärke für web.de
angeben, nach der sich der Werbepreis für Banner berechnet.
Das Pixel ist in jeder Seite von web.de
, und bei einem geladenen Bild wird im HTTP-Header Referer
übermittelt, in welcher Seite das Bild enthalten war.
Dadurch kann man also leicht ermitteln, welche Klickpfade der Benutzer 01914d4d94fc5da00006
auf web.de
so typischerweise hat und was seine Verweildauer auf der Site ist.
Verweildauern sind neben eindeutigen Benutzern wichtig, weil Sites mit langer Verweildauer bessere Werbepreise bekommen (oder was glaubt Ihr, warum es auf Facebook Spiele und Chat gibt? Genau!).
Schließlich kann auch noch weitere Spielchen spielen, falls Javascript aktiviert ist. So findet man etwa bei web.de vor dem Counterpixel den Code
<script type="text/javascript" src="http://uim.tifbs.net/js/4423.js"></script>
der wiederum auf Umwegen die Datei http://uim.tifbs.net/js/global_4423_38.js nachlädt.
Dort wird an geeigneter Stelle ein Aufruf wie folgt gemacht:
this._loadPixel(
this._replaceVariables(
'//pixelbox.uimserv.net/cgi-bin/webde/__TYPE__/__CODE__;sc%3D__SC__%26jsv%3D__JSV__%26scr%3D__SCR__%26flv%3D__FLV__%26vid%3D__VID__%26vct%3D__VCT__%26res%3D__RES__%26smv%3D__SMV__%26cona%3D__CONA__%26cost%3D__COST__?d%3D__D__%26r=__R__'
)
);
Aus dem ganzen Drumherum geht hervor, dass die Texte der Form __NAME__ Platzhalter sind, die Werten ersetzt werden, die zuvor von Javascript eingesammelt werden. FLV ist zum Beispiel die installierte Version von Flash, RES die Bildschirmauflösung und so weiter.
Die komplette Liste findet man in der Funktion _replaceVariables()
in der Javascript-Datei.
Auf diese Weise bekommt pixelbox.uimserv.net
, ein weiterer Zählservice, den web.de
verwendet, nicht nur die Benutzeridentät per Cookie geliefert, sondern über den Pfad der URL und Javascript auch noch einen Haufen weitere Information über den Rechner und seinen Benutzer.
Das Tracking der IVW ist jedoch ein wenig umfassender als man zunächt vermuten mag:
Es wird ja nicht ohne Grund die Domain ivwbox.de
statt web.de
verwendet.
Durch den Namen wird es möglich, den Cookie für .ivwbox.de
zu setzen, also für alle Hosts in der Domain ivwbox.de
.
Wenn ich also zeit.de
aufrufe, wird ein Zählpixel von zeitonl.ivwbox.de
geladen, wie man dort im HTML-Quelltext der Seite nachlesen kann.
Dabei wird meine Identität 01914d4d94fc5da00006
von web.de
ebenfalls wieder an .ivwbox.de
übermittelt.
Auf diese Weise wird dem Betreiber von ivwbox.de
über zeitonl.ivwbox.de bekannt, daß der Benutzer
01914d4d94fc5da00006neben
web.deauch
zeit.de` nutzt.
Dieses Wissen kann man natürlich gut gebrauchen, wenn man Werbebanner verkaufen will.
Über eine Verkettung von Daten könnte man das 01914d4d94fc5da00006
-Pseudonym sogar aufdecken - wenn ich mich bei web.de
einlogge, könnte man das Login kris@web.de
mit der Kennung 01914d4d94fc5da00006
zusammenführen und weiß so, daß kris@web.de
die Zeit online liest.
Das Datenschutz-Nichtproblem - oder doch?
web.de, zeit.de und ivwbox.de sind alles deutsche Rechner, die von deutschen Firmen betrieben werden und man sagt mir, daß das IVW-Tracking mit dem deutschen und europäischen Datenschutzrecht konform ist.
Google Anaytics macht dasselbe, aber deren Server stehen nicht in Deutschland und es ist keine deutsche Firma, und daher steht deutschen Datenschützern gerade Schaum vor dem Mund. Also wohlgemerkt, nicht wegen des Trackings, sondern weil es außerhalb ihres Hoheitsgebietes stattfindet. Wenn es in Deutschland passierte wäre alles gut:
Websites dürfen IP-Adressen nicht ohne Erlaubnis des Nutzers in die USA übermitteln. Daher sei der Einsatz von Google Analytics in der jetzigen Form in aller Regel nach deutschem Recht nicht erlaubt, argumentiert Dix beim Treffen der Google Technology User Group Berlin.
(Hervorherbung von mir)
Zusammenfassung
Cookies sind eine gute Sache und sie zu blockieren verhindert sichere Sessions - und damit Funktionen wie Warenkörbe oder Logins. Cookies können auch verwendet werden, um Benutzer zu tracken oder andere fragwürdige Dinge zu tun. Es ist jedoch nicht der Mechanismus Cookie böse, sondern die jeweilige Anwendung ist zu untersuchen.
Wenn man eine Sicherheitseinstellung im Browser treffen möchte, dann sollte man sogenannte Third Party Cookies unterdrücken. Session-Cookies (jene ohne Expires-Datum) muß man erlauben, dennn sonst funktionieren viele Websites nicht korrekt. Cookies mit Verfallsdatum kann man erlauben, wenn man sich das Leben erleichtern möchte, oder auf “Nachfragen” stellen.
In jedem Fall sollte ein Adblocker zum Einsatz kommen. uBlock origin ist ein Adblocker, der von einer Person ohne kommerzille Interessen entwickelt wird. Er ist auch für Firefox erhältlich.