Server Side Cookies

Heute möchte ich mir mal Cookies vornehmen. Relativ viele Webseiten benutzen diese Datenschnipsel, um alle möglichen Daten über den User zu speichern. Das Problem von Cookies ist, dass alle Cookies, die zu der Domain der aktuell geladenen Seite passen, bei jeder Ressource erstmal vom Client im HTTP-Request geladen werden müssen. So kommt recht schnell eine ordentliche Datenmenge zusammen. In diesem Beitrag möchte ich eine Idee vorstellen, wie man diese Cookiegröße konstant und auch noch konstant klein halten kann.

Anstoß für diesen Beitrag war unter anderem Der Einfluss von Cookies auf die Performance einer Webseite von David Müller. Er beschreibt darin ein übliches Szenario moderner Webseiten:

MeineWebseite.com verpasst euch beim ersten Besuch 5 Cookies, jeder 600 Byte groß. Jetzt besteht die Seite aus 30 Images, 4 Javascripts und 8 CSS-Files – garkein unübliches Setup. Das Dokument selbst noch hinzugerechnet haben wir also 1+30+5+8 = 44 HTTP-Requests.

Ich komme zwar nur auf 43 HTTP-Requests (1+30+4+8), aber das spielt hier auch keine Rolle.

David beschreibt dann die Empfehlung Use Cookie-free Domains for Components, den das Firebug-Plugin YSlow empfiehlt. Hierbei werden alle statischen Komponenten wie Bilder, CSS-Dateien und JS-Dateien in einen anderen Ordner gelegt, auf den man dann mittels einer Subdomain zugreift (z.B. static.example.org). Die Cookies dürfen dazu natürlich nur für die Domain „www.example.org“ gesetzt werden, über die alle dynamischen Webseiten erreichbar sind.

Das ist auch genau so zu empfehlen. Punkt.

Ich habe mir aber in letzter Zeit mal Gedanken gemacht, wie man die Cookie-Größe der Anfragen auf nicht-statische Seiten verkleinern kann und dabei habe ich mir ein System ähnlich den Sessions ausgedacht und das ganze Server Side Cookies genannt. Falls dieses Konzept unter einem anderen Namen bekannt ist, bitte ich um einen kurzen Kommentar.

Das Konzept basiert wie Sessions auf einer ID, die für jeden Besucher eindeutig ist. Diese Cookie-ID wird als ganz normaler Cookie beim User gespeichert. Alle Nutzdaten aber werden serverseitig gespeichert.
Über die Cookie-ID kann dann auf die einzelnen Werte zugegriffen werden.
Diese Methode hat verschiedene Vorteile:

  • konstante Größe der HTTP-Header, egal, wie viele und wie große Daten man in Cookies speichern möchte -> bessere Performance & Skalierung
  • keine Übertragung für die aktuelle Anfrage nutzloser Informationen
  • keine Anzahl- und Größenbeschränkung von Cookies, sonst nur 300 Cookies à max. 4 kB pro Domain erlaubt
  • geringe Anfälligkeit gegen Manipulationen (Validierung der Cookie-Werte muss nur beim Schreiben erfolgen, nicht beim Lesen)

Der einzige Nachteil, der mir eingefallen ist, ist, dass die gespeicherten Informationen mittels der Cookie-ID auf dem Server erstmal ermittelt werden müssen, während sie mit normalen Cookies natürlich sofort vorhanden sind. Inwiefern aber die notwendige Validierung normaler Cookies, die bei den Server Side Cookies nicht nötig ist, diesen zusätzlichen Aufwand bereits auffrisst, kann ich nicht 100%ig sagen.
Weitere Nachteile sind mir erstmal nicht eingefallen, bestimmt gibts aber welche, also bitte ich um einen Kommentar, falls euch weitere einfallen.

Für mein Experiment habe ich folgende Datenbank-Tabelle benutzt:

CREATE TABLE IF NOT EXISTS `cookies` (    `cookie_ID` char(23) NOT NULL, 
`name` varchar(255) NOT NULL,
`value` text NOT NULL, 
`expires` int(10) unsigned NOT NULL, 
PRIMARY KEY (`cookie_ID`,`name`), 
KEY `expires` (`expires`)  ) ENGINE=MyISAM DEFAULT CHARSET=latin1;

Zum Schreiben und Lesen der serverseitigen Cookies habe ich folgenden Code erstellt:

function setServerCookie($name, $value, $expires=null)  {
   if (empty($_COOKIE['id'])) { 
      do {        
         $cookieID = uniqid('', true); 
         $cookie_result = mysql_query("SELECT 1 FROM cookies           
         WHERE cookie_ID='" . $cookieID . "'          
         LIMIT 1");      
         } while (mysql_num_rows($cookie_result) > 0);      
         setcookie('id', $cookieID, time() + 86400 * 30, '/','www.example.org', false, true);    
         } else {      
             $cookieID = $_COOKIE['id'];    
         }      
         if(is_null($expires)){      
             $expires = time()+3600*3;    
         }      
         mysql_query("      
         INSERT INTO cookies (cookie_ID,name,value,expires)
         VALUES ('" . mysql_real_escape_string($cookieID) . "','" . mysql_real_escape_string($name)."','" . mysql_real_escape_string($value) . "','" . intval($expires) . "') 
         ON DUPLICATE KEY UPDATE         
         value='" . mysql_real_escape_string($value) . "',expires='" . intval($expires) . "'" );  
         }    
         function getServerCookie($name)  {    
             if (!isset($_COOKIE['id'])) {      
                  return false;    
             }    
             $cookie_result = mysql_query(" 
             SELECT value 
             FROM cookies
             WHERE cookie_ID='" . $_COOKIE['id'] . "'
             AND name='" . mysql_real_escape_string($name) . "'
             AND expires>=" . time() ."LIMIT 1");    
             if (mysql_num_rows($cookie_result)) {      
                return mysql_result($cookie_result, 0);    
             }    
             return false; 
         }
         

Als Algorithmus für die Erstellung der Cookie-ID nutze ich uniqid(). Außerdem überprüfe ich, ob es nicht eventuell bereits einen anderen Cookie mit der gleichen Cookie-ID gibt. Das schützt davor, dass es im unwahrscheinlichen Falle, dass 2 Besucher die gleiche Cookie-ID erhalten, zu Komplikationen kommt, da dann einer von den beiden eine andere Cookie-ID erhält (die uniqid()-Berechnung wiederholt wird).

Das Verfahren ist natürlich auch auf Dateibasis möglich.

Als Beispiel, was das ganze bringt, nehme ich nochmal die Zahlen von oben: 5 Cookies, jeder 600 Byte groß.
Die Requests für statische Ressourcen sind bereits durch die oben erwähnte Subdomain-Verschiebung eliminiert.
Für den Rest:

  Normale Cookies Server Side Cookies
Größe im HTTP-Header 3000 B 23 Byte

Was haltet ihr von dieser Variante der Speicherung von Cookies?



Deprecated: Directive 'allow_url_include' is deprecated in Unknown on line 0