Cours - Lirmm

Transcription

Cours - Lirmm
IUT Montpellier - Architecture (DU)
V. Poupet
Cours no 5 : Hypertext Transfer Protocol (HTTP)
1
Le protocole HTTP
Lorsqu’un utilisateur (client) veut obtenir une page web, il s’adresse à un serveur web. Il effectue
alors une requête HTTP, qui est suivie d’une réponse HTTP de la part du serveur.
1.1
Requête HTTP
La requête HTTP la plus simple est de la forme :
GET lapin.html HTTP/1.0
Le premier mot (en majuscule) indique la nature de la requête. Ici c’est une requête GET (la plus courante) c’est-à-dire que le client veut obtenir une ressource. Elle est suivie d’une chaîne permettant
d’identifier la ressources (ici on veut le fichier lapin.html) et de la version du protocole utilisé (HTTP
v.1.0).
1.1.1 Structure d’une requête HTTP
Une requête HTTP est constituée d’un en-tête, suivi d’une ligne vide et du corps de la requête.
L’en-tête contient une première ligne contenant la méthode employée, la requête proprement dite
et la version du protocole utilisée. Les lignes suivantes (qui ne peuvent pas être vides) contiennent
différentes informations sous la forme
nom_du_champ: valeur_du_champ
Le contenu de la requête (après la ligne vide) dépend de la méthode utilisée. Ainsi dans le cas d’une
requête GET il est vide, tandis que la méthode POST utilise le corps de la requête pour transmettre les
données.
GET /images/lapin.jpg HTTP/1.1
Accept: image/jpeg, text/html, */*
Accept-Language: fr
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr)
AppleWebKit/418.9.1 (KHTML, like Gecko) Safari/419.3
Connection: keep-alive
Authorization: Basic bGFwaW46dG9ydHVl
Host: localhost:7777
On reconnaît la première ligne (ici le protocole est HTTP v.1.1 et le fichier demandé se trouve dans un
répertoire « images »).
Les trois lignes suivantes indiquent le type de document que le client attend de la part du serveur.
Ici il est indiqué que le client voudrait une image au format jpeg, ou un fichier html ou bien à défaut
n’importe quel type de fichier. Par ailleurs il préfère les documents en langue française (fr) et accepte
que le fichier soit compressé à l’aide de l’un des algorithmes gzip ou deflate.
Le champ User-Agent donne des informations sur le client (navigateur utilisé, système d’exploitation, etc.). Pour des raisons de place sur la page, la ligne a ici été coupée mais elle ne doit pas l’être dans
la requête HTTP.
Le champ Connection indique que la socket utilisée pour la connexion doit être maintenue ouverte après la réponse du serveur. Dans les premières versions du protocole HTTP le serveur fermait la
1
connexion avec le client dès qu’il lui avait envoyé le fichier demandé. Il fallait alors créer une nouvelle
connexion pour le fichier suivant. Aujourd’hui, la plupart des pages sont constituées de nombreux fichiers (un .html et plusieurs images par exemple). Lorsque le client demande la page, il ne recevra que
le .html. En essayant de l’afficher il découvrira alors qu’il lui faut également les autres fichiers et enverra
une demande pour chacun. La demande d’une nouvelle connexion pour chaque élément de la page
ralentit beaucoup la communication et il a été décidé d’ajouter la possibilité de maintenir la connexion
ouverte entre le client et le serveur jusqu’à ce que l’un des deux partis décide explicitement de la fermer
(le client lorsqu’il a obtenu tous les fichiers nécessaires pour afficher la page ou le serveur en cas d’inactivité trop longue du client). Ce comportement keep-alive est maintenant le comportement par
défaut dans le protocole HTTP v.1.1.
Le champ Authorization permet de s’identifier auprès du serveur. Dans l’exemple ci-dessus, on
utilise la méthode Basic qui consiste simplement en l’envoi d’un identifiant et d’un mot de passe. Ces
deux données sont encodées en base 64 afin de s’assurer que les symboles spéciaux pouvant apparaître
ne poseront pas de problème. Dans l’exemple, la chaîne de caractères bGFwaW46dG9ydHVl est l’encodage en base 64 de la chaîne lapin:tortue. Notons que l’information n’est pas cryptée puisque tout
le monde peut décoder cette chaîne (en utilisant un algorithme standard) et que cela représente donc
un risque de sécurité (quiconque intercepte la requête HTTP peut connaître l’identifiant et le mot de
passe).
Le champ Authorization n’est en général pas nécessaire. Il n’est transmis que lorsque l’utilisateur
doit accéder à des informations sensibles. En général on crypte alors la connexion avec le serveur HTTP
en utilisant le protocole SSL afin que le mot de passe ne puisse pas être intercepté.
Enfin, dans notre exemple, le champ Host indique le nom du serveur web que le client veut contacter.
Ce champ, qui était optionnel en HTTP 1.0 mais est devenu obligatoire en HTTP 1.1, peut sembler inutile
puisque le serveur est censé connaître son propre nom. Cependant il est particulièrement utile lorsque
plusieurs noms de serveurs sont associés à la même adresse IP. On peut en effet avoir deux (ou plus)
serveurs serveur1.com et serveur2.com fonctionnant sur la même machine.
Lorsque le client veut obtenir un fichier sur le serveur serveur1.com, il interroge le DNS qui transforme ce nom de serveur en adresse IP. Lorsque la requête HTTP arrive à la machine correspondant à
l’IP demandée cette dernière ne peut pas savoir a priori à quel serveur la demande s’adresse. L’utilisation
du champ Host dans l’en-tête de la requête permet de résoudre ce problème.
Remarque : Il existe encore bien d’autres champs qui peuvent être ajoutés à une requête HTTP, toutefois dans le cas d’une requête de type GET aucun n’est obligatoire en HTTP 1.0 et seul le champ Host
l’est en HTTP 1.1.
1.1.2 Quelques types de requêtes
Il existe huit méthodes (ou verbes) de requêtes HTTP :
GET. C’est la méthode la plus couramment employée. Le client demande au serveur de lui envoyer
une ressource.
HEAD. Semblable à GET dans sa syntaxe, mais ici le client ne demande que les en-têtes de la réponse du
serveur (informations sur la ressource qui aurait été renvoyée, mais pas la ressource elle-même).
Cette méthode peut être utilisée pour vérifier qu’une ressource est disponibles, pour savoir si la
version du serveur est plus récente que celle du client, pour connaître la taille de la ressource, etc.
sans pour autant transmettre la ressource.
POST. Cette méthode est proche de la méthode GET mais ici le client soumet des données (la plupart
du temps entrées à l’aide d’un formulaire HTML) dans le corps de la requête.
PUT. Permet au client d’envoyer une ressource au serveur (modifier un fichier existant par exemple).
Cette méthode est aujourd’hui très peu utilisée car elle présente des risques de sécurité évidents.
On préfère utiliser le protocole FTP pour ce genre de manipulations.
DELETE. Le client demande la suppression d’une ressource. Tout comme la méthode PUT elle n’est
plus utilisée aujourd’hui pour raisons de sécurité.
TRACE. Demande au serveur de renvoyer la requete qu’il reçoit. Cette méthode permet au client de
déterminer les modifications éventuelles appliquées à sa requête par les serveurs intermédiaires
par lesquelles elle est passée.
2
OPTIONS. Le client demande au serveur de lui envoyer la liste des méthodes supportées.
CONNECT. Demande la conversion de la connexion HTTP en un tunnel TCP/IP. Cette méthode est principalement utilisée pour établir des communications cryptées à l’aide du protocole SSL (HTTPS)
lorsque l’on utilise un proxy non crypté.
1.2
Réponse HTTP
La réponse du serveur HTTP à une requête est également constituée d’un en-tête suivi d’une ligne
vide et du corps de la réponse.
À la très simple requête
GET lapin.html HTTP/1.0
pourrait correspondre la réponse (également minimaliste) :
HTTP/1.0 200 OK
Content-Type: text/html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<HTML>
<HEAD>
<TITLE>Lapin</TITLE>
</HEAD>
<BODY>
Ce matin un lapin a tué un chasseur...
</BODY>
</HTML>
On remarque tout d’abord que le corps correspond exactement au corps du fichier demandé (ici un
fichier html mais ce pourrait également être le contenu d’une image ou tout autre type de fichier). C’est
ce qu’avait demandé le client.
L’en-tête quant à lui est constitué de méta-données. La première ligne donne le protocole utilisé (ici
HTTP 1.0), suivi d’un code de retour (trois chiffres et une description). Cette première ligne peut ensuite
être suivie de nombreux champs d’informations.
Remarque : Tout comme dans le cas d’une requête HTTP, la première ligne vide indique au client la
fin de l’en-tête et le début du corps de la réponse.
1.2.1 Les codes de retour
Les codes de retour sont toujours constitués de 3 chiffres. Ils sont divisés en 5 catégories :
1xx. Informations. Par exemple 100 CONTINUE signifie que le serveur a bien reçu l’en-tête de la
requête et qu’il attend le corps (dans le cas d’une requete de type POST par exemple).
2xx. Succès. 200 OK, 202 ACCEPTED, etc.
3xx. Redirection. La ressource n’est pas accessible directement, et le client doit donc effectuer d’autres
tâches avant que la requête ne puisse être complétée. Par exemple 300 MULTIPLE CHOICES
demande à l’utilisateur de préciser sa demande car l’URI correspond à plusieurs ressources différentes, 301 MOVED PERMANENTLY signifie que l’utilisateur doit réitérer sa demande vers une
autre URI, 303 SEE OTHER indique à l’utilisateur de chercher la ressource à une autre URI, etc.
4xx Erreur du client. La requête est mal formulée ou ne peut être exécutée. Les exemples les plus
fréquents sont 400 BAD REQUEST (la requête est mal formulée), 403 FORBIDDEN (le client n’a
pas accès à la ressource) ou encore 404 NOT FOUND (le fichier n’existe pas).
5xx Erreur du serveur sur une requête apparemment valide : 500 INTERNAL SERVER ERROR, 501 NOT IMPLEMENTED
(le serveur ne sait pas traiter ce type de requêtes), etc.
3
1.2.2 Exemple d’en-tête
La plupart du temps le serveur donne de nombreuses informations dans l’en-tête de la réponse HTTP.
On pourra par exemple avoir l’en-tête suivant d’un réponse à une requête de type GET :
HTTP/1.1 200 OK
Date: Thu, 08 Feb 2007 20:48:45 GMT
Server: Apache
Last-Modified: Thu, 08 Feb 2007 19:20:26 GMT
Content-Language: fr
Content-MD5: d72f9499b1b0a9c5c611186e0f98376e
Content-Length: 68975
Content-Type: text/html; charset=iso-8859-1
On trouve donc dans cet en-tête la date (et l’heure) à laquelle la réponse à été envoyée, le type du
serveur web, la date de dernière modification du fichier demandé, la langue du document, le code
MD5 du fichier, la taille du fichier en octets ainsi que le type MIME du document (ici un fichier de type
text, dont le sous-type est html) et le jeu de caractères dans lequel il est encodé.
2
Les serveurs web
Lorsqu’un utilisateur veut obtenir un document sur Internet (la plupart du temps par l’intermédiaire
de son navigateur) il interroge un serveur web qui est chargé de trouver l’information et de la lui
transmettre.
Nous avons vu précédemment comment le navigateur contactait le serveur à l’aide de l’adresse (que
ce soit un nom de domaine ou une adresse IP) et comment la connexion s’établissait à l’aide de sockets
TCP/IP. Nous allons ici détailler les étapes effectuées par le serveur entre la réception de la requête
HTTP et la transmission de la réponse.
Nous prendrons ici comme exemple le serveur Apache HTTP server 1 qui est un serveur libre couramment utilisé (plus de la moitié des sites webs aujourd’hui sont servis par Apache), les autres serveurs
ayant un comportement très similaire.
2.1
Un exemple de requête
Considérons la requête suivante :
POST /dossier/fichier.html HTTP/1.1
Accept: text/html, */*
Accept-Language: fr,en
Accept-charset: iso-8859-1, utf-8
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr)
Authorization: Basic bGFwaW46dG9ydHVl
Connection: keep-alive
Host: www.serveur.org:80
Cette requête, envoyée au serveur www.serveur.org (un autre serveur web refusera la requête car
le champ Host serait alors incorrect) précise que l’utilisateur veut recevoir le fichier /dossier/fichier.html
(la racine de l’arborescence est fixée par le serveur web, ce n’est en général pas la racine du système de
fichiers sur lequel se trouve le serveur). Tous les autres champs ont déjà été présentés précédemment.
Lorsqu’il reçoit une requête HTTP, le serveur Apache exécute alors successivement les étapes suivantes.
1. http://www.apachefrance.com/
4
2.1.1
Child Initialization
Un serveur Apache peut être constitué de plusieurs processus (comme c’est souvent le cas sur un système Unix) ou d’un unique processus contenant plusieurs threads (comme c’est le cas sous Windows).
Quelle que soit son architecture, lorsqu’il reçoit une requête, celle-ci est traitée en parallèle (par un
processus fils ou un nouveau thread) généré à la réception de la requête.
2.1.2
Post Read Request et Header Parser
Cette étape correspond à la lecture de l’en-tête de la requête. Les champs sont parcourus et le serveur
décide alors s’il doit ou non poursuivre le traitement. Si la requête est mal formulée elle peut être rejetée
à cette étape. S’il manque un champ indispensable (comme le champ Host en HTTP 1.1) la demande
est également rejetée.
L’étape Header Parser découpe également les informations contenues dans l’en-tête pour séparer tous
les champs importants (l’URL demandée, les types de retour attendus, etc.) qui seront ensuite transmis
aux étapes suivantes.
2.1.3
Translate Name
L’URL de la ressource demandée est ici transformée en un nom de fichier. Dans notre exemple, si la racine du serveur HTTP se trouve dans le répertoire /home/lapin/web/server/ (dans l’arborescence
de la machine sur laquelle se trouve le serveur) alors le serveur traduit l’URL /dossier/fichier.html
en /home/lapin/web/server/dossier/fichier.html.
Il est également possible de faire faire au serveur des transformations plus complexes. Il est par
exemple possible de demander à ce que toutes les URL du type /script/nom soient transformées
en /usr/bin/nom afin que le serveur puisse exécuter des instructions sur le système de fichier en
recevant des requêtes HTTP.
2.1.4
Check Access
Une fois que le serveur connaît le nom du fichier cible, il vérifie que ce fichier est accessible. Cette
vérification précède toute forme d’authentification de l’utilisateur, c’est-à-dire que le serveur vérifie
simplement que le fichier existe et qu’il pourrait l’obtenir s’il le fallait.
2.1.5
Check User ID
Le serveur doit ici déterminer l’identifiant de l’utilisateur et vérifier que les informations fournies suffisent à confirmer cette identité. Dans notre exemple, le serveur voit que l’authentification se fait selon
le protocole Basic, il décode donc la chaîne de caractères bGFwaW46dG9ydHVl, obtient l’identifiant et
le mot de passe fournis par l’utilisateur (lapin et tortue respectivement) et va demander au système
si l’utilisateur lapin existe et si son mot de passe est bien tortue.
Si cette vérification échoue, le serveur renvoie l’erreur HTTP_UNAUTHORIZED et l’en-tête de la réponse contient les détails indiquant à l’utilisateur quelles informations il doit fournir pour s’identifier
correctement.
2.1.6
Check Auth
La procédure précédente se contente de vérifier l’identifiant annoncé par l’utilisateur (est-il bien celui
qu’il prétend être) mais ne détermine pas pour autant s’il a accès à l’information demandée. C’est l’étape
d’autorisation qui s’en charge.
Connaissant l’identité de l’utilisateur, le serveur peut maintenant vérifier que cet utilisateur a bien
accès à la ressource demandée (la plupart du temps cela se fait en interrogeant le système de fichiers).
Si l’accès n’est pas autorisé, c’est encore une fois l’erreur HTTP_UNAUTHORIZED qui est levée et revoyée à l’utilisateur en lui demandant une nouvelle authentification (sous une autre identité).
5
2.1.7
Type Checker
À ce stade-ci, la requête est presque entièrement traitée. La ressource a été localisée et toutes les
vérifications ont été faites pour s’assurer qu’elle pouvait être transmise au client.
Il ne reste alors plus qu’à déterminer quel handler (module qui manipule le fichier) va effectivement
traiter la demande. En effet, s’il est vrai que la tâche est quasi-instantanée dans le cas d’une requête d’un
fichier HTTP simple (il suffit de joindre le fichier tel quel à la réponse) il est parfois nécessaire de faire
appel à des programmes spécifiques dans des cas plus complexes. Ainsi, si la requête correspond à un
script il faut appeler l’interpréteur correspondant (PHP, Python, Perl, etc.), ou si le fichier demandé doit
être modifié avant d’être envoyé (certaines pages sont traduites avant d’être transmises, ou bien il faut
parfois insérer les champs envoyés pas l’utilisateur dans la page, etc.).
Afin de déterminer le handler approrié le serveur détermine le type MIME de la ressource demandée
à partir de son URL (par exemple text/html, ou image/jpeg) et produit une chaîne de caractères à
transmettre au handler.
Ce module peut également générer une chaîne de caractères à fournir au handler en fonction des
informations connues jusqu’ici (par exemple la ligne de commande d’appel d’un script).
2.1.8
Prerun Fixups
Dernière étape avant de laisser travailler les handlers. C’est ici que sont effectuées les dernières tâches
éventuellement nécessaires au bon fonctionnement du handler, comme par exemple la définition des
variables d’environnement pour un script.
La plupart des modules n’effectuent aucune action à ce stade-ci car tout ce qui devait être fait a été
traité lors de l’une des étapes précédentes.
2.1.9
Handlers
La requête a été entièrement préparée et peut maintenant être effectivement traitée par le programme
approprié. C’est à ce stade-là que les interpréteurs de scripts sont appelés (PHP, Perl, Java, etc.), que
l’on peut traduire le document demandé dans une langue spécifiée par le client, etc.
La plupart des handlers renvoie directement la réponse HTTP (il est alors nécessaire de s’assurer
que le handler ajoute bien l’en-tête de la réponse HTTP), mais ils peuvent aussi parfois se contenter de
transmettre en interne le contenu de la réponse au serveur. Ce dernier devra alors produire l’en-tête.
2.1.10 Logger
Une fois que la requête a été entièrement traitée et que la réponse a été renvoyée au client, le serveur
inscrit dans un fichier de log les détails de la transaction afin d’en garder une trace (par exemple la
totalité de la requête HTTP, les détails de connexion avec l’ordinateur distant et l’en-tête de la réponse
transmise).
2.1.11 Child Exit
Le processus ou thread qui avait été initialisé pour traiter la requête HTTP peut maintenant être
terminé.
3 Sessions HTTP
Le protocole HTTP est un protocole sans état (state-less) c’est-à-dire qu’aucune information n’est
conservée entre deux requêtes successives. Il ne prévoit donc aucune méthode pour spécifier explicitement qu’une requête est la suite d’une autre.
Il est pourtant parfois confortable de pouvoir s’identifier auprès d’un site web et de faire en sorte que
par la suite le serveur reconnaisse l’utilisateur et puisse adapter la réponse à cet utilisateur.
Pour cela, il est nécessaire de mettre en place un susyème de sessions. Une session HTTP est une suite
de requêtes HTTP (d’un client vers un serveur) qui sont reconnues par le serveur comme provenant du
6
même client. Il est alors possible de partager des informations sur la totalité de la session (variables de
session) qui peuvent être lues et modifiées à chaque requête.
3.1
Une première idée : les adresses IP
Afin d’identifier un utilisateur pour suivre ses requêtes successives sur un même serveur, on pourrait
vouloir utiliser l’adresse IP de l’ordinateur client. Notons que cette adresse IP n’est pas donnée dans la
requête HTTP (elle ne figure pas dans les en-têtes) mais que le serveur connaît l’IP de l’ordinateur qui a
demandé la connexion TCP/IP.
Cette technique présente cependant de nombreux inconvénients :
– Si le client utilise un proxy entre son ordinateur et le serveur HTTP, seule l’adresse du proxy sera
visible par le serveur. Certains fournisseurs d’accès à Internet (AOL par exemple qui est le principal FAI aux États-Unis) font passer toutes leurs connexion par un petit nombre de proxys, ce qui
empêche donc les serveurs web de les distinguer par leur adresse IP.
– Un même ordinateur peut être partagé par différents utilisateurs.
– Les ordinateurs sur Internet n’ont pas nécessairement une adresse IP fixe. La plupart des FAI aujourd’hui distribuent dynamiquement les adresses IP et il ne sera donc pas possible pour un serveur de reconnaître un utilisateur si l’ordinateur de ce dernier s’est déconnecté puis reconnecté
avec une autre IP. Notons que de plus en plus de FAI proposent maintenant une option « IP fixe »
à leurs clients.
3.2
La solution HTTP
Il est possible de suivre les requêtes successives d’un même client en utilisant les méthodes GET et
POST du protocole HTTP sur des pages web générées dynamiquement.
En effet, nous avons vu que ces deux méthodes permettent de transmettre des variables lors d’une
requête HTTP : dans le cas de POST les variables et leur valeur sont transmises dans le corps de la
requête tandis que dans le cas de GET elles sont transmises dan l’URI (en ajoutant une chaîne de la
forme ?nom1=val1&nom2=val2).
Lorsqu’un utilisateur arrive sur une page sans fournir de variables dans sa requête le serveur le
considère comme un nouvel utilisateur et lui attribue un numéro de session (différent des numéros déjà
attribués à d’autres utilisateurs).
Une fois ce numéro déterminé (qui servira à identifier cet utilisateur), le serveur modifie tous les liens
des pages qu’il envoie au client pour que celui-ci envoie son numéro de session à chaque fois qu’il suit
un lien. Si par exemple le numéro qui a été attribué est 1032, on ajoute ?id=1032 à tous les liens pour
que l’identifiant soit passé au serveur par la méthode GET à chaque page demandée. Si l’on veut utiliser
la méthode POST il suffit d’ajouter un formulaire caché (qui n’est pas affiché par la page) contenant un
champ pré-rempli indiquant le numéro de session (ce formulaire sera donc transmis à chaque requête
de la part du client).
Il est ensuite facile pour le client d’identifier toutes les requêtes faites par le même client puisqu’elles
contiennent toutes le même identifiant.
Le serveur peut également modifier les valeurs (et le nombre) des variables que le client enverra,
mais un seul identifiant pourrait suffire puisque toutes les variables nécessaires pourraient être enregistrées sur le serveur (toutefois le site pourrait vouloir ne pas encombrer la mémoire du serveur et donc
transmettre les variables aux utilisateurs).
3.2.1 Différences entre POST et GET
Lorsque l’on utilise la méthode GET les variables sont échangées de manière visible dans l’URI. Ceci
peut être intéressant pour permettre à l’utilisateur de modifier facilement les valeurs des variables transmises ou de les échanger avec quelqu’un d’autre en copiant simplement l’URI.
C’est ce qui permet également aux navigateurs de créér des marque-pages dynamiques : on peut marquer la page d’un moteur de recherche en indiquant où doit être insérée la chaîne de caractères à rechercher, ce qui permet d’effectuer une recherche directement sans avoir à passer par la page principale
du moteur de recherche.
7
On pourra cependant préférer cacher ces variables à l’aide de la méthode POST pour différentes raisons. Ça allège les URI affichées par les navigateurs qui sont donc plus faciles à mémoriser et plus
agréables pour l’utilisateur, c’est un moyen de faire passer des variables discrètement sans que l’utilisateur s’en rende compte (il pourrait le voir mais il est rare que l’on inspecte le code source de la page
ou les requêtes HTTP transmises par le navigateur), et enfin ça permet de passer une plus grande quantité d’informations (certains serveurs refusent les URI trop longues, et il est en général déconseillé de
dépasser 255 caractères).
Enfin, remarquons que ces deux méthodes ne se comporteront pas de la même manière vis-à-vis des
marque-pages. Dans le cas d’un passage de variables dans l’URI (GET) si l’utilisateur créé un marque
page , copie entièrement l’adresse, ou l’envoie à ses contacts, les variables seront copiées également et
donc ces mêmes variables (avec leurs valeurs) seront utilisées à chaque fois. Si l’on cache les variables
(POST) elles ne seront pas mémorisées ou transmises.
3.2.2 Inconvénients
Cette méthode permet donc d’implémenter un mécanismes de sessions (et de séquetialité des requêtes d’un client à un serveur) mais elle présente encore certains inconvénients :
– Les sessions ne sont pas conservées si l’utilisateur quitte le site et revient en entrant la même
adresse que la première fois qu’il est venu (donc sans transmettre de variable).
– À l’inverse, comme nous l’avons vu, si l’utilisateur revient sur la page après avoir copié l’adresse
les variables reprendront toutes la valeur qu’elles avaient au moment où l’adresse a été copiée (ou
marquée dans un navigateur).
– Si l’on utilise un proxy qui garde en mémoire les pages web les plus demandées, celui-ci va transmettre à tous les utilisateurs la même page qui ne sera donc pas recréée dynamiquement pour
chacun. Toutes ces pages contiendront donc les mêmes variables et ne permettront pas de distinguer les utilisateurs.
3.3
Cookies HTTP
Les cookies HTTP ont été introduits par le navigateur Netscape afin de pouvoir implémenter correctement un système de sessions. L’objectif était de permettre de gérer un panier de courses virtuel sur
un site d’achats en ligne (l’utilisateur parcourt le site et peut à tout moment ajouter ou supprimer des
objets de son panier virtuel afin de payer la totalité de la commande en une seule fois).
En pratique, les cookies sont des chaînes de caractères que le serveur envoie au client dans l’en-tête
d’une réponse HTTP. Ces cookies sont stockés sur l’ordinateur du client (en général dans un unique
fichier de cookies). Par la suite, à chaque fois que le client enverra une requête à ce serveur, il lui transmettra le cookie dans l’en-tête de la requête HTTP.
Le serveur a la possibilité de modifier ou supprimer le cookie, et peut ajouter des cookies supplémentaires.
3.3.1 Syntaxe côté serveur
Pour placer un cookie chez le client, le serveur utilise le champ Set-Cookie dans l’en-tête de la
réponse HTTP. La syntaxe est la suivante :
Set-Cookie: NOM=VALEUR; expires=DATE; path=CHEMIN;
domain=NOM_DE_DOMAINE; secure
où les mots en minuscule désignent des mots-clés (imposés par le format des cookies) et les mots en
majuscule sont à remplacer par les valeurs choisies par le serveur.
Un cookie a donc plusieurs attributs :
– NOM=VALEUR. C’est le seul champ qui doit obligatoirement être renseigné. Il définit le nom du
cookie et sa valeur. Le nom et la valeur du cookie ne peuvent pas contenir de point virgule, de
virgule ou d’espace. De plus le caractère ’=’ ne peut pas apparaître dans le nom puisque le premier
’=’ rencontré indique la séparation entre le nom et la valeur du cookie (mais la valeur peut contenir
des caractères =).
– expires=DATE. Ce champ indique la limite de validité du cookie. La date est représentée sous la
forme
8
Wed, 06-Feb-2008 18:14:35 GMT
où GMT est le seul fuseau horaire accepté. Souvent les navigateurs enregistrent cette date dans le
fichier de cookies en la mesurant en secondes depuis l’epoch (1er janvier 1970) sous la forme d’un
entier écrit en base 10 (par exemple 1202318075 pour la date indiquée précédemment). Lorsque
le navigateur parcourt la liste des cookies disponibles, il supprime tous ceux qui sont périmés.
C’est également ce mécanisme qui est utilisé par le serveur pour demander la suppression d’un
cookie chez le client : il modifie la date d’expiration à une date quelconque dans le passé.
Il n’est pas indispensable de fournir une date d’expiration. Si aucune n’est fournie le cookie n’est
valable que pour la session courante et est donc détruit lors de la fermeture du navigateur.
– domain=NOM_DE_DOMAINE. Indique le domaine de validité du cookie. Le navigateur ne transmettra le cookie que lorsque la requête HTTP est transmise à un sous-domaine de celui indiqué.
Ainsi, si par exemple le navigateur reçoit un cookie de domaine domaine.com, il le transmettra à
chaque fois qu’il contactera le serveur domaine.com, mais également s’il contacte mon.domaine.com
ou moi.mon.domaine.com. Le serveur qui envoie le cookie doit nécessairement appartenir au
domaine spécifié sans quoi le navigateur refuse le cookie.
Par ailleurs il existe des contraintes sur le domaine d’un cookie qui imposent une certaine spécificité, ainsi il n’est pas possible de fixer un cookie dont le domaine est com ou co.uk qui sont trop
génériques.
Si le domaine n’est pas précisé, c’est le domaine du serveur qui est considéré par défaut.
– path=CHEMIN. Cet attribut précise l’ensemble des URI qui sont concernées par le cookie. En effet le cookie ne sera transmis que si l’URI demandée commence par le contenu du champ path.
Par exemple, si le chemin est /un/chemin, le cookie sera envoyé lorsque l’on demande le fichier
/un/chemin/index.html, mais également si l’on demande /un/cheminot.
Si le chemin n’est pas spécifié lorsque le cookie est envoyé il est considéré comme se rapportant au
chemin « / » qui inclut toutes les pages du domaine.
– secure. Cet attribut (qui n’a pas de valeur) indique simplement que le cookie ne doit être renvoyé
par le navigateur que si la connexion utilisée est cryptée. Si cet argument n’est pas donné à la
création du cookie, le cookie est envoyé quelle que soit la sécurité de la connexion. Cet attribut est
utilisé lorsque le cookie représente une information sensible (une identification sur un site bancaire
par exemple).
3.3.2 Syntaxe côté client
Lorsque le client envoie une requête, le navigateur parcourt la liste des cookies et détermine ceux
qui correspondent à la requête émise (la date d’expiration n’est pas passée, le domaine et le chemin
correspondent, et la sécurité de la connexion est adaptée). Il transmet ensuite les noms et valeurs de
chacun de ces cookies en une seule ligne de l’en-tête de la requête HTTP sous la forme :
Cookie: NOM1=VALEUR1; NOM2=VALEUR2; ...
3.3.3 Remarques
– Il est possible d’envoyer plusieurs cookies dans une unique réponse HTTP en utilisant plusieurs
champs Set-Cookie dans l’en-tête.
– Lorsqu’un cookie portant le même nom et correspondant aux mêmes domaine et chemin qu’un
cookie existant est envoyé, il remplace le précédent. Si par contre les chemins sont différents les
deux cookies sont conservés.
– Le client peut choisir de détruire un cookie à tout moment (même si la date limite n’est pas atteinte).
Inversement, il peut choisir de continuer à utiliser un cookie périmé.
– Lorsque le client envoie les cookies au serveur, les cookies correspondant aux chemins les plus
spécifiques doivent être envoyés en premier. Ceci est particulièrement important dans le cas où
deux cookies de même nom mais de chemin différent co-existent. Ainsi si le client a deux cookies
nom=val1 de chemin / et nom=val2 de chemin /blop/, le cookie nom=val2 doit apparaître en
premier dans la liste envoyée.
– Le nombre et la taille des cookies sont limités. Cette limite varie en fonction du navigateur. De
manière générale, on considère les limites suivantes :
– le navigateur ne conserve pas plus de 300 cookies au total ;
9
– la chaîne formée du nom et de la valeur d’un cookie ne doit pas dépasser 4096 caractères ;
– seuls 20 cookies par domaine sont acceptés.
– Les serveurs doivent considérer que les navigateurs clients ne dépasseront pas ces limites. Lorsqu’un cookie est reçu alors que la limite est atteinte le navigateur est censé supprimer le cookie
dont la dernière utilisation est la plus ancienne.
– Par ailleurs si le navigateur reçoit un cookie dont la taille dépasse 4096 caractères, il doit le tronquer
en conservant le nom.
– Les serveurs proxy qui utilisent un cache pour stocker les réponses HTTP ne doivent pas enregistrer les champs Set-Cookie.
10

Documents pareils