Je ziet dit bericht omdat de EU dat een goed idee vindt. Deze website maakt gebruik van cookies van Google voor het tonen van advertenties en het bijhouden van bezoekersstatistieken. Google kan hiermee je surfgedrag volgen. Zie voor meer informatie het privacybeleid van Google. Via Your Online Choices kun tracking cookies van advertentiebedrijven blokkeren. Deze melding verbergen.

11 Cookies

Cookies zijn kleine tekstbestandjes die in de webbrowser van de bezoeker kunnen worden opgeslagen, om ze op een later tijdstip weer uit te lezen. Ze maken het mogelijk informatie te onthouden en te gebruiken op verschillende pagina's van je website. Cookies kunnen voor vele doeleinden worden gebruikt, waaronder het realiseren van een login-gedeelte op een website.

Theorie

In dit hoofdstuk bekijken we hoe een cookie wordt opgeslagen in de webbrowser van de bezoeker en hoe het weer uitgelezen kan worden. Ook kijken we naar wat technische kanten van cookies die handig zijn om in het achterhoofd te houden.

Beperkingen en veiligheid

Cookies kennen een instelbare geldigheid die zelf gekozen kan worden. Als de geldigheid verloopt wordt het cookie automatisch bij de bezoeker verwijderd. Hierbij zijn er twee mogelijkheden: een cookie laten verwijderen als de bezoeker zijn browser sluit, of een specifieke einddatum opgeven. In het laatste geval wordt het cookie door de browser bewaard, ook als deze tussendoor wordt gesloten. Hou er rekening mee dat een bezoeker altijd de mogelijkheid heeft zijn cookies te verwijderen. Ook zijn cookies gebonden aan één specifieke browser op één specifiek gebruikersaccount op één specifieke computer (sommige browsers kunnen cookies meenemen tussen verschillende installaties door koppeling aan een gebruikersaccount, maar dat laten we hier even buiten beschouwing). Er kan dus nooit van uit worden gegaan dat een geplaatst cookie altijd blijft bestaan. Informatie die niet verloren mag gaan kan dus beter niet in een cookie worden opgeslagen.

Omdat cookies tekstbestandjes zijn die op de computer van de bezoeker worden opgeslagen, zijn ze vrij eenvoudig te vervalsen. Zo kan een bezoeker de inhoud en de geldigheid van een cookie wijzigen. Informatie in een cookie is dus niet per definitie te vertrouwen! Dit is een belangrijk gegeven als het gaat om het maken van beveiligde gedeeltes op een website. Het praktijkvoorbeeld komt hier nog op terug.

Cookie plaatsen

De functie setcookie() maakt het mogelijk om een cookie op de computer van de bezoeker op te slaan:

bool setcookie ( string $name [, string $value [, int $expire = 0 [, string $path [, string $domain [, bool $secure = false [, bool $httponly = false ]]]]]] )

Belangrijk: een cookie kan alleen worden opgeslagen als nog geen ander output naar de browser is verzonden. De functie setcookie() moet dus worden gebruikt voor je doctype, je <html>, je <head>, etc. Er mag zelfs geen spatie voor de PHP-openingstag <?php staan!

De parameters van setcookie() zijn achtereenvolgens $name voor de naam van het cookie. De hier gekozen naam gebruik je ook weer voor het uitlezen van het cookie. Wat je wil opslaan in het cookie wordt gegeven in $value. De geldigheid van het cookie wordt bepaald door $expire. Geef hier waarde 0 (nul) om aan te geven dat het cookie moet worden verwijderd als de bezoeker zijn browser sluit. Voor een langere geldigheid wordt de verloopdatum gegeven als UNIX tijdstempel. De laatste interessante parameter is $path. Standaard geldt een cookie voor de huidige map en alle mappen die zich daar in bevinden. Om een cookie geldig te maken voor de hele website (domein) wordt $path ingesteld op waarde '/'.

Laten we eens een eenvoudig voorbeeld bekijken. Onderstaande code plaatst een cookie met de naam phpboek_cookie en Hallo wereld! als inhoud. De geldigheid van het cookie is ingesteld op 0, dus het cookie is weg als de bezoeker de browser sluit.

<?php
$c_naam
= 'phpboek_cookie';
$c_waarde = 'Hallo wereld!';

setcookie($c_naam, $c_waarde, 0, '/');
?>

In onderstaand voorbeeld geven we het cookie een zelf gekozen geldigheid mee.

<?php
$c_tijd
= time() + 604800;

setcookie($c_naam, $c_waarde, $c_tijd, '/');
?>

Hierbij gebruiken we de functie time().

int time ( void )

Deze functie geeft het UNIX tijdstempel van dit moment (in seconden). De tijd die het cookie geldig moet blijven wordt hier bij opgeteld. Om een cookie een week geldig te laten moet er dus 604800 seconden bij opgeteld worden.

Cookie lezen

Een eenmaal geplaatst cookie kan worden uitgelezen via de globale variabele $_COOKIE[]. Deze array bevat alle cookies die door de website geplaatst zijn. De zelf gekozen naam van een cookie komt terug als sleutel in de array $_COOKIE[]. Hou er rekening mee dat een geplaatst cookie pas bij de volgende pagina die de bezoeker opvraagt beschikbaar is in $_COOKIE[]. Plaatsen en direct uitlezen in hetzelfde script is dus niet mogelijk.

Het eerder geplaatste cookie kunnen we dus als volgt uitlezen:

<?php
$c_naam
= 'phpboek_cookie';
echo
htmlspecialchars($_COOKIE[$c_naam]);
?>

Omdat niet zeker is dat de inhoud van een cookie hetzelfde is als wat er in is gezet, gebruiken we htmlspecialchars() om eventuele code-injecties onschadelijk te maken.

Cookie verwijderen

Er is geen functie om een cookie te verwijderen. In plaats daarvan wordt een nieuw cookie gezet met dezelfde naam. Dit keer echter zonder inhoud en een geldigheidsdatum in het verleden. Het eerder geplaatste cookie kan als volgt worden verwijder:

<?php
$c_naam
= 'phpboek_cookie';

setcookie($c_naam, '', 1, '/');
?>

Als tijdstip voor geldigheid is het UNIX tijdstempel 1 gekozen. Dit is het tijdstip 01-01-1970 00:00:01, zo ver in het verleden als mogelijk is. Er kan ook een ander tijdstempel worden gekozen, zo lang dit maar in het verleden ligt is het effect hetzelfde.

Array opslaan in cookie

Soms is het nodig om meerdere verschillende waarden in een cookie op te slaan. Het is mogelijk om voor iedere waarde een apart cookie te maken, maar het is netter om alles wat je nodig hebt voor je website in één cookie te bewaren. Plaats hiervoor alle waarden die je wilt bewaren in een array, en sla de array op in het cookie. Echter, als we terug kijken naar de parameters van de functie setcookie(), dan moet de informatie die in het cookie wordt opgeslagen een string zijn en geen array!

Om de array te converteren naar een string kan de functie serialize() gebruikt worden.

string serialize ( mixed $value )

Deze functie converteert (onder andere) een array op een dusdanige manier naar een string zodat er later weer een array van gemaakt kan worden. Hiermee kunnen we een array in een cookie opslaan.

<?php
$gebruiker['naam'] = "phpboek";
$gebruiker['id'] = 21;
$gebruiker['key'] = "3492080e224e475afd747dd89fb01607";

//serialize de array
$cookie_value = serialize($gebruiker);
//zet geserializede array in cookie
setcookie("cookie", $cookie_value, time() + 3600, '/');
?>

Als het cookie wordt opgehaald kan van de geserializeerde array weer een echte array gemaakt worden met hulp van de functie unserialize(). De werking hiervan is gelijk als die van zijn tegenhanger.

mixed unserialize ( string $str )

De oorspronkelijke array kan hiermee als volgt uit het cookie van het vorige voorbeeld worden teruggehaald:

<?php
$gebruiker = unserialize($_COOKIE['cookie']);
?>

Praktijkvoorbeeld: loginscript

Eén van de meest voorkomende toepassingen van cookies zijn beveiligde gedeeltes van een website. Daarbij wordt een cookie gebruikt om de bezoeker te identificeren en eventueel bij een volgend bezoek automatisch in te loggen. Een dergelijk loginscript bestaat feitelijk uit drie delen: een script om de gebruiker te laten aanmelden met gebruikersnaam en wachtwoord, een script om te bepalen of gebruiker daadwerkelijk is aangemeld, en een script om de gebruiker af te melden. Dit praktijkvoorbeeld gaat in op een heel eenvoudig loginscript. In geval van een website met veel gebruikers wordt meestal een database gebruikt, maar dat doen we hier nog niet.

Aanmeldscript

Het script om aan te melden bestaat uit een HTML-formulier voor het invoeren van gebruikersnaam en wachtwoord, een deel dat controleert of gebruikersnaam en wachtwoord correct zijn en een deel dat het cookie plaatst. Als basis gebruiken we het volgende HTML-formulier:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Login</title>
</head>
<body>

    <h1>Login</h1>
    <p>Vul gebruikersnaam en wachtwoord in om toegang te krijgen tot het beveiligde gedeelte van deze website</p>
    
    <form method="post">
    <table>
    <tr><td>Gebruikersnaam:</td><td><input type="text" name="username"></td></tr>
    <tr><td>Wachtwoord:</td><td><input type="password" name="password"></td></tr>
    <tr><td></td><td><input type="submit" value="Login"></td></tr>
    </table>
    </form>
    
</body>
</html>

Het formulier is eenvoudig en bevat velden voor gebruikersnaam en wachtwoord. Er is geen formulier-actie opgegeven, dus post naar zichzelf (zie ook het praktijkvoorbeeld van hoofdstuk 10). Omdat een cookie alleen geplaatst kan worden als nog geen andere uitvoer naar de browser is gestuurd, betekent dit dat we de aanmeld controle helemaal bovenaan het bestand moeten toevoegen.

Voordat we dat doen maken we eerst een apart bestand met alle gebruikersnamen en bijbehorende wachtwoorden. Deze slaan we op in een array, waarbij de sleutel gelijk is aan de gebruikersnaam en het wachtwoord de bijbehorende waarde. Het bestand slaan we op als users.inc.php en gaan we gebruiken voor zowel het aanmeldscript als het script dat gaat controleren of iemand is ingelogd.

<?php
//Lijst met gebruikersnamen en wachtwoorden.
$gebruikers = array(
    
'jantje' => 'FHASSVAE',
    
'pietje' => 'DSFHEASD',
    
'keesje' => 'SJSAFSAE'
);
?>

De controle voor de gebruikersnaam en wachtwoord is als volgt: eerst kijken we of er een gebruikersnaam en wachtwoord zijn ingevuld; zo ja, dan wordt de ingevulde gebruikersnaam gebruikt als sleutel voor de array $gebruikers en het hierbij gevonden wachtwoord vergeleken met het ingevulde wachtwoord. Hierbij wordt het eerder gemaakte bestand users.inc.php met behulp van include() opgenomen in het script.

<?php
include('users.inc.php');

//als formulier verzonden
if (!empty($_POST['username']) && !empty($_POST['password'])) {
    
//controleer wachtwoord
    
if ($gebruikers[$_POST['username']] == $_POST['password']) {
        
$cookie['username'] = $_POST['username'];
        
$cookie['password'] = hash('sha256', $_POST['password']);
        
//zet cookie
        
setcookie('login', serialize($cookie), time() + 60*60*24*7*2, '/');
        
//login is gelukt
        
$login_correct = TRUE;
    }
    
//wachtwoord niet correct
    
else {
        
$login_error = TRUE;
    }
}
?>

Als gebruikersnaam en wachtwoord correct zijn, dan worden beide als array in een cookie opgeslagen. Hierdoor kan de gebruikersnaam/wachtwoord controle uitgevoerd worden op andere pagina's van de website zonder dat de gebruiker steeds gebruikersnaam en wachtwoord moet opgeven. Omdat cookies als tekst worden opgeslagen op de computer van de gebruiker is het niet slim het wachtwoord open en bloot in het cookie te zetten. Dat maakt het voor kwaadwillenden heel eenvoudig om een wachtwoord uit een cookie te vissen. Daarom wordt het wachtwoord gecodeerd opgeslagen in het cookie, waarvoor gebruik wordt gemaakt van de functie hash().

string hash ( string $algo , string $data [, bool $raw_output = false ] )

De eerste parameter van hash() is het te gebruiken algoritme. Welke algoritmes precies beschikbaar zijn hangt af van de PHP installatie, maar normaliter zit het hier gekozen sha256 er wel tussen. SHA256 is een cryptografische hashfunctie. Gegeven een berekende uitkomst is het niet praktisch mogelijk het originele wachtwoord terug te berekenen. De beveiliging is hiermee niet 100% – er zijn tabellen om wachtwoorden op te zoeken die bij een bepaalde hash horen – maar voor het doel van een simpele websitebeveiliging goed genoeg. Voor het doel van dit praktijkvoorbeeld gaat het echter te ver om nu op alle details hiervan in te gaan.

Het wachtwoord wordt door hash('sha256', $_POST['password']) opgeslagen als 64 (hexadecimale) tekens. Daarnaast wordt er gebruik gemaakt van twee hulpvariabelen om de bezoeker te laten weten of de ingevulde informatie goed of fout was. Hiervoor moet de HTML pagina met het loginformulier nog wat worden aangepast:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Login</title>
</head>
<body>

<?php if ($login_correct === TRUE) { ?>
    
    <h1>Login gelukt!</h1>
    <p>Welkom in het beveiligde gedeelte van deze website!</p>
    
<?php } else { ?>
    
    <h1>Login</h1>
    <p>Vul gebruikersnaam en wachtwoord in om toegang te krijgen tot het beveiligde gedeelte van deze website</p>
    
    <?php
    
if ($login_error === TRUE) {
        echo
'<p class="error">De gebruikersnaam/wachtwoord combinatie bestaat niet.</p>';
    }
    
?>
    
    <form method="post">
    <table>
    <tr><td>Gebruikersnaam:</td><td><input type="text" name="username"></td></tr>
    <tr><td>Wachtwoord:</td><td><input type="password" name="password"></td></tr>
    <tr><td></td><td><input type="submit" value="Login"></td></tr>
    </table>
    </form>
    
<?php } ?>

</body>
</html>

Nu wordt bij een correcte login getoond dat dit het geval is. Voor praktisch gebruik kunnen hier wat links opgenomen worden naar beveiligde onderdelen van de website. Als de login niet gelukt is wordt het formulier getoond met foutmelding. Het totale script wordt daarmee als volgt:

<?php
include('users.inc.php');

//als formulier verzonden
if (!empty($_POST['username']) && !empty($_POST['password'])) {
    
//controleer wachtwoord
    
if ($gebruikers[$_POST['username']] == $_POST['password']) {
        
$cookie['username'] = $_POST['username'];
        
$cookie['password'] = hash('sha256', $_POST['password']);
        
//zet cookie
        
setcookie('login', serialize($cookie), time() + 60*60*24*7*2, '/');
        
//login is gelukt
        
$login_correct = TRUE;
    }
    
//wachtwoord niet correct
    
else {
        
$login_error = TRUE;
    }
}
?>

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Login</title>
</head>
<body>

<?php if ($login_correct === TRUE) { ?>
    
    <h1>Login gelukt!</h1>
    <p>Welkom in het beveiligde gedeelte van deze website!</p>
    
<?php } else { ?>
    
    <h1>Login</h1>
    <p>Vul gebruikersnaam en wachtwoord in om toegang te krijgen tot het beveiligde gedeelte van deze website</p>
    
    <?php
    
if ($login_error === TRUE) {
        echo
'<p class="error">De gebruikersnaam/wachtwoord combinatie bestaat niet.</p>';
    }
    
?>
    
    <form method="post">
    <table>
    <tr><td>Gebruikersnaam:</td><td><input type="text" name="username"></td></tr>
    <tr><td>Wachtwoord:</td><td><input type="password" name="password"></td></tr>
    <tr><td></td><td><input type="submit" value="Login"></td></tr>
    </table>
    </form>
    
<?php } ?>

</body>
</html>

Login controle

We kunnen nu inloggen waarbij een cookie wordt geplaatst. Maar zolang het cookie nergens wordt gecontroleerd is er nog niets beveiligd. In deze paragraaf bekijken we een eenvoudig controle-script dat alleen maar hoeft te worden toegevoegd bovenaan de broncode van de pagina's die moeten worden beveiligd. Hiervoor moeten we onze lijst met gebruikersnamen en wachtwoorden hebben, de inhoud van het cookie lezen en controleren of de gegevens in het cookie overeen komen met de lijst. De code daarvoor ziet er als volgt uit:

<?php
include('users.inc.php');
$cookie = unserialize($_COOKIE['login']);
if (
hash('sha256', $gebruikers[$cookie['username']]) != $cookie['password']) {
    echo
'Niet ingelogd!';
    exit;
}
?>

De controle werkt als volgt: met behulp van de gebruikersnaam uit het cookie wordt het hierbij behorende wachtwoord gelezen uit de array $gebruikers. Dit wachtwoord wordt gecodeerd met behulp van hash(), waarbij hetzelfde SHA256 algoritme wordt gebruikt als bij het plaatse van het cookie. De verkregen hash wordt dan vergeleken met de hash die in het cookie is opgeslagen. Zijn deze niet gelijk, dan is de gebruiker niet ingelogd! Als de bezoeker niet is ingelogd wordt dit gemeld en wordt het uitvoeren van het script gestopt met behulp van exit:

void exit ([ string $status ] )

exit zorgt ervoor dat de uitvoering van het script direct wordt gestopt. Alles wat na exit staat wordt genegeerd en ook niet meer naar de bezoeker gestuurd (omdat exit geen echte functie maar een taalconstructie is hoeven er geen haakjes gebruikt te worden, mag wel). De te beveiligen pagina is hiermee dus afgeschermd; enkel bovenaan dit stukje script toevoegen.

Afmeldscript

Nu dat bezoekers kunnen inloggen, is het ook handig om ze de mogelijkheid te geven om ze te laten uitloggen. Zeker omdat het cookie twee weken geldig is. De code hiervoor is bijzonder simpel: het cookie verwijderen wat eerder gezet is. Slechts één regel code!

<?php
setcookie('login', '', 1, '/');
?>

Zelftest

  1. De inhoud van een cookie kan worden uitgelezen via:
    1. setcookie()
    2. getcookie()
    3. $_COOKIE[]
    4. $COOKIE[]
  2. Wanneer is de inhoud van een cookie betrouwbaar?
    1. Nooit.
    2. Alleen als de geldigheid niet is verstreken.
    3. Alleen als een cookie wordt gezet dat niet automatisch met het sluiten van de browser verwijderd wordt.
    4. Altijd.
  3. Welke van onderstaande stellingen is/zijn waar?
    I             Het is mogelijk een cookie te plaatsen met een onbeperkte geldigheid.
    II            Om een cookie te verwijderen wordt een geldigheid in het verleden ingesteld.
    1. Alleen stelling I is waar.
    2. Alleen stelling II is waar.
    3. Beide stellingen zijn waar.
    4. Beide stellingen zijn onwaar.
  4. De geldigheid van een cookie wordt uitgedrukt in:
    1. seconden vanaf het moment van plaatsen.
    2. seconden vanaf 1 januari 1970.
    3. minuten vanaf het moment van plaatsen.
    4. minuten vanaf 1 januari 1970.

Antwoorden zelftest

Antwoorden

  1. c
  2. a
  3. b
  4. b

Oefening: Cookie plaatsen en lezen

De volgende array wordt gegeven:

$array['naam'] = "phpboek";
$array['laatste_bezoek'] = time();

Opdracht 1: cookie plaatsen

Schrijf het script om de array op te slaan in een cookie. Het cookie moet twee weken geldig blijven.

Opdracht 2: cookie lezen

Schrijf een afzonderlijk script om het cookie uit te lezen en weer te geven zoals het volgende voorbeeld:

Naam: phpboek
Laatste bezoek: 1370727753

Uitwerking opdracht

Uitwerking

opdracht1.php

<?php
$array
['naam'] = "phpboek";
$array['laatste_bezoek'] = time();

setcookie('phpboek', serialize($array), time() + 1209600, '/');
?>

opdracht2.php

<?php
$array
= unserialize($_COOKIE['phpboek']);

echo
'Naam: '.$array['naam'];
echo
'<br />';
echo
'Laatste bezoek: '.$array['laatste_bezoek'];
?>

De cookiewet

In Nederland is de Telecommunicatiewet aangepast op 5 juni 2012, waarin bepalingen rond het gebruik van cookies zijn opgenomen. Wat betekent dit nu voor je website?

De "cookiewet" maakt onderscheid tussen cookies die noodzakelijk zijn voor het functioneren van de website en cookies die dat niet zijn. Onder noodzakelijke cookies vallen bijvoorbeeld cookies die nodig zijn om in te loggen of cookies die de inhoud van de winkelwagen van een webshop onthouden. Voor niet-noodzakelijke cookies heeft de wetgever bepaald dat vooraf toestemming moet worden gevraagd om deze te plaatsen. Voor noodzakelijke cookies hoeft dat niet. Onder niet-noodzakelijke cookies vallen ook cookies die gebruikt worden om bezoekersstatistieken bij te houden. Dus ook daarvoor moet volgens de wet toestemming worden gevraagd!

Minister Kamp heeft op 20 mei 2013 in een brief aan de Tweede Kamer een voorstel gedaan tot aanpassing van de cookiewet. Als deze wijziging wordt geaccepteerd hoeft in de toekomst alleen nog maar toestemming gevraagd te worden voor cookies die de privacy van bezoekers mogelijk in het geding brengen. Het bijhouden van bezoekersstatistieken kan normaal gesproken zonder de privacy van de bezoekers in gevaar te brengen, dus als de cookiewet wordt gewijzigd hoeft hiervoor geen toestemming meer te worden gevraagd. Als van een externe dienst voor bezoekersstatistieken gebruik wordt gemaakt, zoals bijvoorbeeld Google Analytics, is het wel van belang deze dienst te toetsen aan de cookiewet.

Moet toestemming gevraagd worden voor het gebruik van een cookie? Dan wordt om de bezoeker niet iedere keer te hoeven vragen de voorkeur vastgelegd in, jawel, een cookie.