Services Web

Contenu en cours de développement

  • HTTP/1.1
  • Apache 2.4 en Debian 8 et en RHEL 7
  • Nginx en Debian 8 et en RHEL 7
  • MariaDB et PostgreSQL
  • Sécurité / audit / OWASP

Objectifs de certification

RHCE EX300

  1. HTTP/HTTPS
    • 3.1. Configure a virtual host.
    • 3.2. Configure private directories.
    • 3.3. Deploy a basic CGI application.
    • 3.4. Configure group-managed content.
    • 3.5. Configure TLS security.

LPI 202

HTTP/1.1

Sources et crédits

Ce document reprend les textes suivants en licence CC ou GFDL.

  • https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol
  • https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
  • http://www.bortzmeyer.org/7230.html
  • http://www.bortzmeyer.org/http-11-reecrit.html
  • http://www.bortzmeyer.org/7231.html
  • http://www.rfc-editor.org/rfc/rfc7230.txt
  • http://www.rfc-editor.org/rfc/rfc7231.txt

    Le texte s'inspire très largement des articles de Stéphane Bortzmeyer en attendant une adaptation plus personnelle du propos si cela s'avère nécessaire. Que ce dernier soit remercié pour sa prose passionnante.

    1. Présentation du protocole HTTP

    L'HyperText Transfer Protocol, plus connu sous l'abréviation HTTP — littéralement « protocole de transfert hypertexte » — est un protocole de communication client-serveur développé pour le World Wide Web. HTTPS (avec S pour secured, soit « sécurisé ») est la variante du HTTP sécurisée par l'usage des protocoles SSL ou TLS.

    HTTP est un protocole de la couche application. Il peut fonctionner sur n'importe quelle connexion fiable, dans les faits on utilise le protocole TCP comme couche de transport. Un serveur HTTP utilise alors par défaut le port TCP 80 (et TCP 443 pour HTTPS).

    Les clients HTTP les plus connus sont les navigateurs Web permettant à un utilisateur d'accéder à un serveur contenant les données. Il existe aussi des systèmes pour récupérer automatiquement le contenu d'un site tel que les aspirateurs de site Web ou les robots d'indexation.

    Ces clients se connectent à des serveurs HTTP tels qu'Apache HTTP Server ou Internet Information Services (IIS).

    1.1. Historique

    HTTP a été inventé en 1989 par Tim Berners-Lee avec les adresses Web et le langage HTML pour créer le World Wide Web. À cette époque, le File Transfer Protocol (FTP) était déjà disponible pour transférer des fichiers, mais il ne supportait pas la notion de format de données telle qu'introduite par Multipurpose Internet Mail Extensions (MIME).

    La première version de HTTP était très élémentaire, mais prévoyait déjà le support d'en-têtes MIME pour décrire les données transmises. Cette première version reste encore partiellement utilisable de nos jours, connue sous le nom de HTTP/0.9.

    En mai 1996, HTTP/1.0 voit le jour et est décrit dans le RFC 1945. Cette version supporte les serveurs HTTP virtuels, la gestion de cache et l'identification.

    En janvier 1997, HTTP/1.1 devient finalement standard de l'IETF. Il est décrit dans le RFC2068 de l'IETF, puis dans la RFC2616 en juin 1999. Cette version ajoute le support du transfert en pipeline (ou pipelinage) et la négociation de type de contenu (format de données, langue).

    En mars 2012, les travaux à propos de HTTP/2.0 démarrent à l'IETF adoptant SPDY comme matériel de départ.

    En février 2014, la spécification de HTTP 1.1 a été republiée. Elle a été éclatée en huit RFC et corrigée pour toutes ses imprécisions, RFC7230 à RFC7237 :

  • RFC 7230, qui décrit l'architecture, les URI, et la syntaxe des messages,

  • RFC 7231, qui décrit la sémantique des messages, les codes de retour à trois chiffres, les en-têtes des requêtes et des réponses,
  • RFC 7232, sur les requêtes conditionnelles,
  • RFC 7233, normalise les requêtes demandant une portion d'un contenu, en spécifiant un intervalle,
  • RFC 7234, décrit le fonctionnement des caches Web,
  • RFC 7235, spécifie les mécanismes d'authentification de HTTP,
  • RFC 7236, enregistre les anciens mécanismes d'authentification, qui avaient été spécifiés avant le RFC 7235,
  • RFC 7237, enregistre les anciennes méthodes HTTP, pour initialiser le registre.

    HTTP/2 a été publié sous le RFC7540 en mai 2015.

    1.2. Objectifs

    HTTP, un des protocoles les plus célèbres de l'Internet, permet à des clients d'accéder à des ressources situées sur des serveurs. (Le terme de « ressource » a été choisi car il est abstrait : les ressources peuvent être des fichiers mais ce n'est pas forcément le cas.)

    1.3. Caractéristiques

  • HTTP est sans état, chaque requête est indépendante des autres et un serveur peut répondre à une requête sans forcément connaître la séquence des requêtes précédentes.

  • Comme il est très générique, et ne suppose pas grand'chose sur les clients et les serveurs, HTTP peut être utilisé dans un grand nombre de contextes différents.
    • Son utilisation par les navigateurs Web n'est donc qu'une seule possibilité. HTTP est utilisé, côté client,
    • par des appliances,
    • des programmes non-interactifs (mise à jour du logiciel, par exemple),
    • des applications tournant sur mobile et récupérant des données sans que l'utilisateur le voit, etc.
  • De même, le modèle du serveur HTTP Apache tournant sur un serveur Unix dans un data center n'est qu'un seul modèle de serveur HTTP. On trouve de tels serveurs dans les caméras de vidéo-surveillance, les imprimantes, et bien d'autres systèmes.

    1.3. HTTP est un protocole, pas une implémentation

    Il faut notamment se souvenir qu'il n'y a pas forcément un humain dans la boucle. C'est pourquoi certaines propositions d'évolution de HTTP qui nécessitaient une interaction avec un utilisateur humain, par exemple pour désambiguïser des noms de domaine, sont absurdes. Même chose pour les décisions de sécurité.

    Il existe de nombreuses passerelles vers d'autres systèmes d'information. Un client HTTP peut donc, via une passerelle, accéder à des sources non-HTTP. D'une manière générale, HTTP étant un protocole, et pas une implémentation, le client ne sait pas comment le serveur a obtenu la ressource et où. Au tout début du Web, le seul mécanisme pour le serveur était de lire un fichier, mais ce n'est plus le cas depuis bien longtemps (d'où l'utilisation du terme « ressource » et pas « fichier » dans la norme). HTTP spécifie donc un comportement extérieur, pas ce qui se passe à l'intérieur de chaque machine.

    2. Architectures HTTP

    2.1. Architecture du WWW

    La section 2 du RFC 7230 décrit l'architecture du World-Wide Web et notamment de HTTP.

    HTTP est un protocole requête/réponse, sans état. Un client interroge un serveur, au-dessus d'un protocole de transport fiable, TCP. Comme dans tout protocole client/serveur, le serveur attend passivement des requêtes et les traite lorsqu'elles arrivent. Les ressources sont identifiées par un URI (normalisés dans le RFC 3986).

    Le format des messages HTTP est du texte, comme avec bien d'autres protocoles TCP/IP, par exemple SMTP. Cela facilite l'écriture des programmes, et surtout leur déboguage (messages tapés à la main, lecture des communications). À noter que la prochaine version de HTTP, HTTP 2, utilisera au contraire un encodage binaire. Ce format texte ressemble à bien des égards à l'IMF du RFC 5322, notamment pour la syntaxe des en-têtes (Name: value). HTTP emprunte aussi à MIME par exemple pour indiquer le type des ressources (texte, image, etc).

    Le cas le plus simple en HTTP est la récupération d'une ressource par une requête GET. En voici un exemple, affiché par le client HTTP curl dont l'option -v permet de visualiser les requêtes et les réponses. Le client envoie la ligne GET suivie du chemin de la ressource sur le serveur, le serveur répond par une ligne de statut, commençant par le fameux code à trois chiffres (ici, 200). Client et serveur peuvent et, dans certains cas, doivent, ajouter des en-têtes précisant leur message :

    % curl -v http://www.bortzmeyer.org/files/exemple-de-contenu.txt
    

    Requête envoyée :

    > GET /files/exemple-de-contenu.txt HTTP/1.1
    > User-Agent: curl/7.26.0
    > Host: www.bortzmeyer.org
    > Accept: */*
    >
    

    Réponse reçue :

    < HTTP/1.1 200 OK
    < Date: Thu, 29 May 2014 16:35:44 GMT
    < Server: Apache/2.2.22 (Debian)
    < Last-Modified: Fri, 11 Nov 2011 18:05:17 GMT
    < ETag: "4149d-88-4b1795d0af140"
    < Accept-Ranges: bytes
    < Content-Length: 136
    < Vary: Accept-Encoding
    < Link: rel="license"; title="GFDL"; href="http://www.gnu.org/copyleft/fdl.html"
    < Content-Type: text/plain; charset=UTF-8
    
    [Fin des en-têtes, le contenu de la ressource suit]
    

    C'est juste un exemple de texte ("contenu"), rien de particulier. Il est uniquement en ASCII, pour contourner les histoires d'encodage.

Ceci était le cas le plus simple : HTTP permet des choses bien plus compliquées. Ici, pour une page en HTML avec davantage de champs dans la réponse :

 % curl -v http://www.hackersrepublic.org/

Résultat :

 > GET / HTTP/1.1
 > User-Agent: curl/7.26.0
 > Host: www.hackersrepublic.org
 > Accept: */*
 >
 < HTTP/1.1 200 OK
 < Server: Apache/2.4.6
 < X-Powered-By: PHP/5.4.4-14+deb7u9
 < X-Drupal-Cache: HIT
 < Content-Language: french
 < X-Generator: Drupal 7 (http://drupal.org)
 < Cache-Control: public, max-age=0
 < Expires: Sun, 19 Nov 1978 05:00:00 GMT
 < Etag: "1401374100-0-gzip"
 < Last-Modified: Thu, 29 May 2014 14:35:00 GMT
 < Content-Type: text/html; charset=utf-8
 < Vary: Cookie,Accept-Encoding
 < Transfer-Encoding: chunked
 < Date: Thu, 29 May 2014 16:37:15 GMT
 < Connection: keep-alive
 < Via: 1.1 varnish
 < Age: 0
 <
 ...
 <!DOCTYPE html>
 <head>
 <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
 <meta charset="utf-8" />
 <link rel="apple-touch-icon-precomposed" href="http://www.hackersrepublic.org/sites/all/modules/touch_icons/apple-touch-icon-precomposed.png" type="image/png" />
 <link rel="apple-touch-icon" href="http://www.hackersrepublic.org/sites/all/modules/touch_icons/apple-touch-icon.png" type="image/png" />
 <meta name="viewport" content="width=device-width" />
 <meta name="Generator" content="Drupal 7 (http://drupal.org)" />
 ...

2.2. Intermédiaires HTTP

  • Proxy
  • Reverse Proxy
  • Tunnels

    Une des complications possibles est la présence d'intermédiaires. HTTP permet des relais des passerelles et des tunnels.

    Le relais (proxy) est du côté du client, souvent choisi par lui, et transmet les requêtes, après avoir appliqué certains traitements, comme le filtrage de la publicité, la censure, ou bien la mise en cache (cf. RFC 7234) des ressources souvent demandées, pour accélérer les requêtes suivantes (c'est par exemple la principale fonction de l'excellent logiciel Squid) et c'est un excellent moyen d'économiser de la capacité réseau, particulièrement lorsqu'on est connecté par des lignes lentes).

    Lorsque le relais n'est pas explicitement choisi par le client, on parle de transparent proxy (RFC 1919 et RFC 3040). Ils servent typiquement à restreindre les services auquel un utilisateur captif peut accéder.

    La passerelle (gateway, également nommée reverse proxy, et qu'il ne faut pas confondre avec celle décrite plus haut qui fait la conversion entre HTTP et un autre protocole) est, au contraire, proche du serveur, choisie par lui, et fournit des services comme la répartition de charge ou comme la mémorisation des réponses, pour aller plus vite la prochaine fois (c'est par exemple le rôle du logiciel Varnish dont vous avez vu la présence signalée par l'en-tête Via: dans l'exemple précédent).

    Enfin, le tunnel assure juste une transmission des octets d'un point à un autre. Il est surtout utilisé pour le cas où la communication est chiffrée par TLS mais que le client et le serveur ne peuvent pas se parler directement.

    2.3. Composantes du WWW

    Le World-Wide Web repose sur trois piliers,

  • le protocole HTTP, présenté ici,

  • le langage HTML,
  • et les adresses des ressources, les URI, normalisées dans le RFC 3986.

    3. Plans (scheme) d'URI HTTP

    HTTP utilise deux plans (scheme) d'URI,

  • http:

  • et https:

    Le plan http: est spécifique à TCP, bien que HTTP ait juste besoin d'un canal fiable et ne se serve pas des autres fonctions de TCP.

    L'adresse IP de la (ou des) machine(s) est typiquement trouvée dans le DNS. Ainsi, ce blog est en http://www.bortzmeyer.org/ ce qui veut dire qu'il faudra faire une requête DNS pour le nom www.bortzmeyer.org (http://www.bortzmeyer.org/ est un URI, www.bortzmeyer.org est un nom de domaine). Le port) par défaut est le bien connu 80.

    Le plan https: est pour les connexions HTTP sécurisées avec TLS (le petit cadenas du navigateur Web...) Le port est alors le 443. TLS est normalisé dans le RFC 5246.

    4. Format des messages HTTP

Le format des messages est :

  • Une ligne de départ,
  • puis une syntaxe inspirée de l'IMF du RFC 5322, avec ses champs « Nom: valeur »,
  • puis une ligne vide puis un corps optionnel.

    Le récepteur va en général lire la ligne de départ, puis lire les en-têtes en les mettant dans un dictionnaire, puis, si l'analyse de ces données montre qu'un corps peut être présent, le récepteur va lire le corps pour la quantité d'octets indiquée, ou bien jusqu'à la coupure de la connexion.

    La ligne de départ est la seule dont la syntaxe est différente entre les requêtes et les réponses.

    Pour une requête, on trouve une méthode (la liste des méthodes possibles est dans le RFC 7231), une cible, puis la version HTTP.

    Pour la réponse, on a la version HTTP, le code de retour (les fameux trois chiffres), et une raison exprimée en langue naturelle. Voici un exemple avec curl, où on récupère une ressource existante, avec la méthode GET et on a le code de retour 200 (succès) :

    % curl -v http://www.afnic.fr/
    

    Résultat :

    > GET / HTTP/1.1
    > User-Agent: curl/7.32.0
    > Host: www.afnic.fr
    > Accept: */*
    >
    < HTTP/1.1 200 OK
    < Date: Tue, 22 Apr 2014 16:47:34 GMT
    < Server: Apache/2.2.3 (Red Hat) DAV/2 mod_ssl/2.2.3 OpenSSL/0.9.8e-fips-rhel5
    < Expires: Thu, 19 Nov 1981 08:52:00 GMT
    < Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
    < Pragma: no-cache
    < Content-Type: text/html; charset=utf-8
    < Set-Cookie: afnic-prod=m3nc4r1oivltbdkd9qbh6emvr5; path=/
    < Transfer-Encoding: chunked
    <
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1
    -transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    ...
    

    Ici, par contre, on essaie de détruire (méthode DELETE) une ressource qui n'existe pas. On a le code de retour 404 (ressource inexistante) :

    % curl -v -X DELETE http://www.afnic.fr/test
    

    Résultat :

    > DELETE /test HTTP/1.1
    > User-Agent: curl/7.32.0
    > Host: www.afnic.fr
    > Accept: */*
    >
    < HTTP/1.1 404 Not Found
    < Date: Tue, 22 Apr 2014 16:50:16 GMT
    < Server: Apache/2.2.3 (Red Hat) DAV/2 mod_ssl/2.2.3 OpenSSL/0.9.8e-fips-rhel5
    < Expires: Thu, 19 Nov 1981 08:52:00 GMT
    < Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
    < Pragma: no-cache
    ...
    

    Les codes de retour possibles sont décrits en détail dans le RFC 7231 (voir plus bas)

    5. Routage des requêtes

    Lorsqu'un client HTTP reçoit un URL, qu'en fait-il ? Il va regarder si la ressource correspondant à cet URL est déjà dans sa mémoire et est réutilisable.

    Si non, il va regarder s'il doit faire appel à un relais (cela dépend de la configuration dudit client).

  • Si oui, il se connecte au relais et fait une requête HTTP où l'identificateur de ressource est l'URL complet (absolute form dans le RFC).
  • Si non, il extrait le nom du serveur HTTP de l'URL, se connecte à ce serveur, et fait une requête HTTP où l'identificateur de ressource est juste la partie « chemin ». Le champ Host: de l'en-tête HTTP vaut le nom du serveur. Le port par défaut (s'il n'est pas indiqué dans l'URL) est, comme chacun le sait, 80 (et 443 pour HTTPS). Le nom de serveur donné dans l'URL est directement utilisé pour une requête de résolution de noms pour avoir l'adresse.

    À noter que le RFC 7230 ne couvre pas l'autre partie du « routage », le fait, pour le serveur, de trouver, pour une cible donnée, la localisation de la ressource demandée.

    Les premiers serveurs HTTP avaient un routage très simple : la cible était préfixée par un nom de répertoire configuré dans le serveur, et le tout était interprété comme le chemin d'un fichier sur le serveur. Ainsi, GET /toto.html sur un serveur où le nom de départ était /var/web, servait le fichier /var/web/toto.html.

    Aujourd'hui, ce mécanisme de routage existe toujours mais il est accompagné de nombreux autres. À noter que, depuis la création du concept de virtual host, le serveur HTTP commence par chercher le virtual host, en utilisant le champ Host: pour le routage.

    6. Gestion des connexions

    HTTP n'a pas besoin de grand'chose de la part du protocole de transport sous-jacent : juste une connexion fiable, où les octets sont reçus dans l'ordre envoyé. TCP convient à ce cahier des charges et c'est le protocole de transport utilisé lorsque l'URL est de plan http: ou https:.

    L'établissement d'une connexion TCP prenant un certain temps (la fameuse triple poignée de mains), il est logique que les connexions soient persistentes et réutilisables.

    Un client HTTP peut aussi avoir plusieurs connexions TCP ouvertes simultanément vers le même serveur mais le RFC lui impose de limiter leur nombre. (Ce parallélisme est utile pour éviter qu'une courte requête, par exemple pour une feuille de style soit bloquée par un gros téléchargement.)

7. Requêtes, réponses et ressources

  • Un message HTTP est soit une requête, soit une réponse.

  • Requête ou réponse sont composées d'une première ligne, puis d'une série de champs (formant l'en-tête de la requête ou de la réponse) et éventuellement d'un corps.

  • La première ligne d'une requête est une méthode (comme GET), qui donne le sens principal de la requête (l'en-tête pouvant légèrement modifier cette sémantique) et ses paramètres,
  • la première ligne d'une réponse est surtout composée d'un code de retour, les fameux trois chiffres.

    Les méthodes des requêtes (comme GET ou POST) agissent sur des ressources.

    7.1. Ressources HTTP

    La ressource, vous l'avez vu, est une notion assez abstraite. On ne peut interagir avec elle que via l'étroite interface de HTTP, sans savoir comment le serveur à l'autre bout gère les ressources (fichier ? extraction dynamique d'une base de données ? autre processus ?) Cette abstraction est à la base du principe « REST ». Mais la ressource a une représentation, qui est une suite d'octets, quelque chose de concret, donc. Une même ressource peut avoir plusieurs représentations. Un exemple simple est celui où la ressource est une image et où il y a une représentation en JPEG, une en PNG, etc. Les différentes représentations seront des suites d'octets complètement différentes les unes des autres alors qu'elles représenteront « la même » image.

    Le choix de la représentation est fait par le mécanisme dit de « négociation du contenu ».

    Les représentations sont étiquetées avec un type de média (dit aussi type MIME) à la syntaxe bien connue « type/sous-type » comme image/png. En plus du type et du sous-type, ils peuvent contenir des paramètres comme le charset (terme impropre car c'est en fait un encodage), charsets qui sont enregistrés à l'IANA, suivant le RFC 2978. Le tout est mis dans le champ Content-type: comme, par exemple :

    Content-Type: text/html; charset=UTF-8
    

    Malheureusement, les serveurs HTTP ne sont pas toujours correctement configurés et les étiquettes de type/sous-type peuvent être incorrectes. Certains navigateurs Web tentent de résoudre le problème en analysant la représentation (ce qu'on nomme le « content sniffing ») mais cette pratique, peu fiable, est déconseillée, notamment pour des raisons de sécurité (il existe des logiciels malveillants encodés de façon à sembler une image GIF pour certains logiciels et un exécutable Windows pour d'autres).

    Outre ce type/sous-type, la représentation a d'autres métadonnées. Par exemple, on peut indiquer une langue, soit dans la requête (la langue qu'on veut), soit dans la réponse (la langue obtenue). La langue est codée par une étiquette de langue (RFC 5646) comme fr, az-Arab ou en-AU. En pratique, demander des langues spécifiques n'a guère d'intérêt car la qualité de la traduction n'est pas prise en compte. Si je préfère le français, mais que je peux lire l'anglais, une demande dans cet ordre me donnera surtout des pages Web mal traduites en français.

    7.2. Méthodes HTTP

    Certaines méthodes sont sûres, c'est-à-dire qu'elles sont en lecture seule : elles ne modifient pas les ressources sur le serveur. On peut donc les utiliser sans modération. Les méthodes peuvent être idempotentes, c'est-à-dire que leur application répétée produit un résultat identique à une application unique. Toute méthode sûre est idempotente (puisqu'elle ne change pas la ressource) mais l'inverse n'est pas vrai. Enfin, certaines méthodes sont qualifiées de « cachables » (désolé pour l'affreux terme, et qui est faux en plus car il ne s'agit pas de dissimuler quoi que ce soit, c'est une allusion aux caches dans les réseaux). Les réponses peuvent potentiellement être gardées en mémoire pour resservir. Toutes les méthodes sûres sont cachables.

    Méthode GET

    La reine des méthodes, la première définie, la plus courante est évidemment GET. C'est la méthode par défaut de la plupart des clients (par exemple, avec curl, c'est celle qui sera utilisée si on ne met pas l'option -X/--request).

    Elle demande au serveur d'envoyer une représentation de la ressource indiquée. Dans le cas du serveur HTTP le plus simple, les URI sont traduits en noms de fichiers locaux (et la syntaxe des URI reflète la syntaxe des noms de fichiers Unix) et ces fichiers sont alors simplement envoyés au client. Mais on peut mettre en œuvre GET de bien d'autres façons. GET est sûre et donc idempotente et cachable.

    Méthode HEAD

    Utilisée surtout pour le déboguage, la méthode HEAD ne transfère pas la représentation, mais uniquement le code de retour et les en-têtes de la réponse. Cela permet de tester un serveur sans épuiser la capacité réseau, par exemple dans un programme de vérification de liens. HEAD est sûre et donc idempotente et cachable. (Attention, certaines applications Web boguées renvoient un code de succès alors même qu'elles ont un problème ; pour vérifier le bon fonctionnement d'une telle application, il faut faire un GET et analyser le contenu, comme avec les options -r ou -s du check_http des plugins Nagios.)

    Méthode POST

    Au contraire, POST n'est pas sûre : elle demande qu'on traite le contenu de la requête (avec GET, la requête n'a pas de contenu, juste l'en-tête) dans le cadre d'une ressource donnée. Son utilisation la plus connue est le cas où la ressource visée est un formulaire et où la requête contient les valeurs qui vont être placées dans les champs. Dans certains cas, POST est cachable (mais, en pratique, peu de logiciels de cache en profitent).

    Méthode PUT

    Plus radical, PUT remplace la ressource par le contenu de la requête (ou bien crée une ressource si elle n'existait pas déjà).

    Elle n'est évidemment pas sûre mais elle est idempotente (le résultat, qu'on applique la requête une fois ou N fois, sera toujours une ressource dont la représentation est le contenu de la requête).

    Méthode PUT et POST

    Le code de retour (voir plus bas) sera différent selon que la ressource a été créée ou simplement remplacée.

  • Dans le premier cas, le client récupérera un 201,

  • dans le second un 200.

PUT et POST sont souvent confondus et on voit souvent des API REST qui utilisent POST (plus courant et plus connu des développeurs) pour ce qui devrait être fait avec PUT.

La différence est pourtant claire : avec un PUT, la ressource sur le serveur est remplacée (PUT est donc idempotente), alors qu'avec POST elle est modifiée pour intégrer les données envoyées dans le corps du POST.

Voici un exemple de PUT avec l'option -T de curl (qui indique le fichier à charger) :

 % curl -v -T test.txt http://www.example.net/data/test.txt

Résultat :

 > PUT /data/test.txt HTTP/1.1
 > User-Agent: curl/7.37.0
 > Host: www.example.net
 > Accept: */*
 > Content-Length: 7731
 ...
 < HTTP/1.1 201 Created
 < Server: nginx/1.6.0
 < Date: Fri, 30 May 2014 20:38:36 GMT
 < Content-Length: 0
 < Location: http://www.example.net/data/test.txt

(Le serveur nginx était configuré avec dav_methods PUT;.)

Méthode DELETE

La méthode DELETE permet de supprimer une ressource stockée sur le serveur, comme le ferait le rm sur Unix.

Méthode CONNECT

La méthode CONNECT est un peu particulière car elle n'agit pas réellement sur une ressource distante : elle dit au serveur de créer un tunnel) vers une destination indiquée en paramètre et de relayer ensuite les données vers cette destination. Elle sert lorsqu'on parle à un relais Web et qu'on veut chiffrer le trafic de bout en bout avec TLS.

Par exemple :

 CONNECT server.example.com:443 HTTP/1.1
 Host: server.example.com:443

va se connecter au port 443 de server.example.com.

Méthodes OPTIONS et TRACE

Restent les méthodes OPTIONS et TRACE qui servent pour l'auto-découverte et le déboguage. Rarement mises en œuvre et encore plus rarement activées, vous trouverez peu de serveurs HTTP qui les gèrent.

7.3. En-têtes des requêtes

Ces en-têtes permettent au client HTTP d'envoyer plus de détails au serveur, précisant la requête.

D'abord, les en-têtes de contrôle. Ce sont ceux qui permettent de diriger le traitement de la requête par le serveur. Le plus connu est Host:, défini dans le RFC 7230.

  • En-têtes des requêtes conditionnelles
  • En-têtes de négociation de contenu
  • En-têtes de langues
  • En-têtes d'authentification

    Relevons une dernière catégorie d'en-têtes qui est représentée par les en-têtes de contexte, qui donnent au serveur quelques informations sur son client.

    Ils sont trois,

  • From: qui contient l'adresse de courrier électronique de l'utilisateur. Il n'est guère utilisé que par les robots, pour indiquer une adresse à laquelle se plaindre si le robot se comporte mal. En effet, son envoi systématique poserait des gros problèmes de protection de la vie privée.

  • Le deuxième en-tête de cette catégorie est Referer: qui indique l'URI où le client a obtenu les coordonnées de la ressource qu'il demande. (À noter que le nom est une coquille ; en anglais, on écrit referrer.) Si je visite l'article de Wikipédia sur le Chaperon Rouge et que j'y trouve un lien vers http://www.example.org/tales/redridinghood.html, lors de la connexion au serveur www.example.org, le navigateur enverra :

    Referer: http://fr.wikipedia.org/wiki/Le_Petit_Chaperon_rouge
    

    Cet en-tête pose lui aussi des problèmes de vie privée. Il peut renseigner le serveur sur l'historique de navigation, les requêtes effectuées dans un moteur de recherche, etc. Notamment, le navigateur ne doit pas envoyer cet en-tête si l'URI de départ était local, par exemple de plan file:.

  • Enfin, User-Agent:, le troisième en-tête de contexte, permet d'indiquer le logiciel du client et son numéro de version. Comme certains sites Web, stupidement, lisent cet en-tête et adaptent leur contenu au navigateur (une violation hérétique des principes du Web), les navigateurs se sont mis à mentir de plus en plus, comme le raconte une jolie histoire. Par exemple, le navigateur que j'utilise en ce moment envoie :

    User-Agent: Mozilla/5.0 (X11; Linux i686; rv:29.0) Gecko/20100101 Firefox/29.0 Iceweasel/29.0.1
    

    (Au passage, si vous voulez voir tout ce que votre navigateur envoie, vous pouvez essayer ce service.)

    Si vous utilisez Apache, et que vous voulez conserver, dans le journal, la valeur de certains en-têtes rigolos, Apache permet de le faire pour n'importe quel en-tête. Ainsi :

    LogFormat "[%h]:%{remote}p %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %v" combinedv6
    

    va enregistrer le Referer: et le User-Agent: ce qui donnera :

    [2001:db8:22::864:89]:37127 - - [12/Jun/2014:10:09:17 +0200] "GET /greylisting.html HTTP/1.1" 200 3642 "http://fr.wikipedia.org/wiki/Greylisting" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36" www.bortzmeyer.org
    

    7.4. Codes de retour

    On a déjà parlé du code de retour HTTP, les fameux trois chiffres qui indiquent si la requête a réussi ou pas. La section 6 le décrit plus en profondeur. Ce code est composé d'une classe, indiquée par le premier chiffre, et d'un code particulier dans les deux chiffres suivants. Des nouveaux codes sont régulièrement créés et un client HTTP doit donc se préparer à rencontrer de temps en temps des codes inconnus. En revanche, le nombre de classes est fixe. Ce sont :

    1xx

  • 1xx : codes informatifs indiquant que la requête a été reçue mais le travail demandé n'est pas encore terminé (par exemple 100 qui signifie « patientez deux secondes, ça arrive » ou 101 lorsqu'on utilise WebSocket).

    2xx

  • 2xx : la requête est un succès (le code le plus fréquent est 200 « tout va bien, voici ta réponse » mais il y en a plusieurs autres comme 201 indiquant que la ressource n'existait pas mais a été créée avec succès, par exemple par un PUT).

    3xx

  • 3xx : codes de redirection, indiquant que le client va devoir aller voir ailleurs pour terminer sa requête (300 pour indiquer qu'il y a plusieurs choix possibles et que le client doit se décider). 301 et 302 permettent désormais de changer la méthode utilisée (POST en GET par exemple) 307 et 308 ne le permettent pas. 301 et 308 sont des redirections permanentes (le navigateur Web peut changer ses signets#Navigation_internet)), les autres sont temporaires. Si vous utilisez Apache, la directive Redirect permet de faire des 301 (Redirect temp) ou des 302 (Redirect permanent), pour les autres, il faut indiquer explicitement le code (cf. la documentation). Attention à bien détecter les boucles (redirection vers un site qui redirige...)

    4xx

  • 4xx : erreur située du côté du client, qui doit donc changer sa requête avant de réessayer. C'est par exemple le fameux 404, « ressource non trouvée » ou le non moins célèbre 403 « accès interdit ». À noter que, si vous êtes administrateur d'un serveur et que vous savez que la ressource a définitivement disparu, vous pouvez envoyer un 410, qui indique une absence définitive (Redirect gone /PATH dans Apache, au lieu d'un simple Redirect mais ce n'est pas forcément respecté.) Ah, et si vous voyez un 402, sortez vos bitcoins, cela veut dire Payment required.

    5xx

  • 5xx : erreur située du côté du serveur, le client peut donc essayer une requête identique un peu plus tard (c'est par exemple le 500, « erreur générique dans le serveur » lorsque le programme qui produisait les données s'est planté pour une raison ou l'autre).

    La liste complète des codes enregistrés (rappelez-vous qu'elle est parfois allongée) est stockée à l'IANA mais c'est plus rigolo de regarder la fameuse page des codes HTTP représentés par des chats, où les images ont été très bien choisies (ce sont des images de cette collection qui sont affichées par ce blog en cas d'erreur). Il existe aussi une page équivalente avec des chiens.

    7.5. En-têtes de réponse

    Derrière la première ligne de la réponse, celle qui contient ce code de retour en trois chiffres, les en-têtes de réponse. La section 7 du RFC les décrit en détail. Là encore, plusieurs catégories.

    Contrôle

    La première est celle du contrôle. C'est le cas de Date: qui indique date et heure du serveur. Le format de cette information est un sous-ensemble de celui du RFC 5322 (et, hélas, pas du RFC 3339, bien plus simple et lisible). À noter qu'on trouve parfois des serveurs utilisant d'autres formats : c'était mal spécifié au début de HTTP. Un exemple avec le format recommandé :

    % curl -v http://www.hackersrepublic.org/
    

    Résultat :

    < HTTP/1.0 200 OK
    < Server: Apache/2.4.6
    < Date: Sat, 14 Jun 2014 12:11:19 GMT
    

    Location: sert en cas de redirection à indiquer le nouvel URI. Par exemple :

    % curl -v http://www.bortzmeyer.org/eusthatius-test-grammars.html
    ...
    > GET http://www.bortzmeyer.org/eusthatius-test-grammars.html HTTP/1.1
    

    Résultat :

    < HTTP/1.0 301 Moved Permanently
    < Date: Sat, 14 Jun 2014 12:13:21 GMT
    < Location: http://www.bortzmeyer.org/eustathius-test-grammars.html
    

    (Redirection mise en place suite à une coquille dans le lien depuis un site important.)

    Le champ Vary: est plus subtil. Il indique de quels paramètres de la requête dépend le résultat obtenu. C'est indispensable pour les caches : si une réponse varie selon, mettons, la langue demandée, un autre client qui demande une autre langue ne doit pas recevoir le même contenu, même si l'URL est identique. Un cache Web doit donc utiliser comme clé d'une ressource, non pas l'URL seul mais la combinaison de l'URL et du contenu de Vary:. Voici un exemple sur ce blog, où le format d'image peut être négocié :

    % curl -v http://www.bortzmeyer.org/images/nat66
    

    Résultat :

    > GET /images/nat66 HTTP/1.1
    > Accept: */*
    ...
    < HTTP/1.1 200 OK
    < Content-Location: nat66.gif
    < Vary: negotiate,accept
    ...
    

    C'est la version GIF qui a été choisie et le Vary: indique bien que cela dépendait de l'en-tête Accept:.

    Validateurs

    Deuxième catégorie de réponses, les validateurs, comme Last-Modified:. Leur utilisation principale est pour des requêtes conditionnelles ultérieures (RFC 7232). Ainsi, une réponse avec un Last-Modified:, indiquant la date de dernier changement, permettra au client de demander plus tard « cette ressource, si elle n'a pas changé depuis telle date », limitant ainsi le débit réseau si la ressource est inchangée. Autre en-tête validateur, Etag:, dont la valeur est une étiquette (entity tag) identifiant de manière unique une version donnée d'une ressource. Ainsi :

    % curl -v https://www.laquadrature.net/fr/snowden-terminator-et-nous
    

    Résultat :

    < HTTP/1.1 200 OK
    < ETag: "da6e32e8d35ff7cf11f9c83d814b9328"
    ...
    

    La ressource snowden-terminator-et-nous de ce serveur est identifiée par l'étiquette da6e32e8d35ff7cf11f9c83d814b9328 (probablement un condensat MD5).

    Autres catégories des en-têtes de réponse

    Il y a deux autres catégories pour les en-têtes de réponse, la troisième comprend les en-têtes utilisées pour l'authentification (RFC 7235) comme WWW-Authenticate:. Et la quatrième est composée des en-têtes indiquant le contexte. La plus connue est Server: qui indique le(s) logiciel(s) utilisé(s) par le serveur. Par exemple, dans le cas de ce blog (et changeons un peu, utilisons wget au lieu de curl) :

    % wget --server-response --output-document /dev/null http://www.bortzmeyer.org/
    

    Résultat :

    HTTP/1.1 200 OK
    Server: Apache/2.2.22 (Debian)
    ...
    

    7.6. Mises-à-jour des codes et listes HTTP

    Toutes ces listes de codes, en-têtes, etc, ne sont pas figées. Régulièrement, de nouveaux RFC les mettent à jour et la version faisant autorité est donc stockée dans un registre à l'IANA. La section 8 rappelle la liste de ces registres :

  • Un nouveau registre pour les méthodes (GET, PUT, etc, le RFC 7237 enregistre formellement les anciennes méthodes). Les éventuelles nouvelles méthodes doivent être génériques, c'est-à-dire s'appliquer à tous les genres de ressources. Lors de l'enregistrement, il faudra bien préciser si la méthode est idempotente, sûre, etc.

  • Un autre registre pour les codes de retour comme 200 ou 404. L'ajout d'un nouveau code nécessite le processus IETF review décrit dans le RFC 5226, section 4.1.

  • Encore un autre pour les en-têtes, qu'ils soient dans les requêtes ou dans les réponses. Ce registre est partagé avec d'autres protocoles qui utilisent un format similaire, notamment le courrier électronique. Les procédures sont celles du RFC 3864. Autrefois, il était fréquent de définir des en-têtes sans les enregistrer, en les préfixant d'unX-. Cette pratique a été abandonnée par le RFC 6648.

  • Et enfin un dernier registre pour le codage du contenu (en fait pas tout à fait le dernier, certains sont omis ici).

    8. Sécurité HTTP

    8.1. Indications sur la version du logiciel et sécurité

    Contrairement à une idée reçue, les indications sur la version du logiciel que transporte cet en-tête ne posent guère de problèmes de sécurité. Les attaquants ne s'y fient pas (ils savent que cet en-tête peut être modifié par l'administrateur du serveur et que, de toute façon, la vulnérabilité n'est pas liée à une version, certains systèmes patchent le logiciel mais sans changer le numéro de version) et essaient donc toutes les attaques possibles (le serveur HTTP qui héberge ce blog reçoit souvent des tentatives d'attaques exploitant des failles d'IIS, alors que c'est un Apache et qu'il l'annonce)

    8.2. Autorité des réponses

    D'abord la question de l'autorité que fait (ou pas) la réponse. Les problèmes de sécurité surviennent souvent lorsque l'idée que se fait l'utilisateur ne correspond pas à la réalité : c'est le cas par exemple du hameçonnage où la réponse qui fait autorité, pour HTTP, n'est pas celle que croit l'utilisateur.

    Le RFC donne quelques conseils comme de permettre aux utilisateurs d'inspecter facilement l'URI (ce que ne font pas les utilisateurs et que les navigateurs Web ne facilitent pas, trop occupés à noyer la barre d'adresses, jugée trop technique, au milieu d'autres fonctions).

    Mais il peut aussi y avoir des cas où HTTP lui-même est trompé, par exemple si unempoisonnement DNS ou bien une attaque contre le routage IP a envoyé le navigateur vers un autre serveur que celui demandé.

    8.3. HTTPS

    HTTPS vise à résoudre ces problèmes mais, avec l'expérience qu'on a maintenant de ce service, on peut voir que ce n'est pas si simple en pratique (attaques contre les AC, bogues dans les mises en œuvre de TLS, etc).

    Et cela ne résoud pas le problème de l'utilisateur qui suit aveuglément un lien dans un courrier reçu... À noter que HTTP n'a aucun mécanisme d'intégrité), pour se protéger contre une modification du message. Il dépend entièrement des services sous-jacents, TLS dans le cas de HTTPS. Ces services protègent le canal de communication mais pas les messages eux-mêmes, pour lesquels il n'y a pas de sécurité de bout en bout, encore une sérieuse limite de HTTPS. Même chose pour la confidentialité (le groupe de travail, après de longues discussions n'a pas réussi à se mettre d'accord sur un texte à inclure au sujet de l'interception des communications HTTP.)

    8.4. Attaque basée sur le nom de fichier

    D'abord, l'attaque basée sur le nom de fichier. Si un serveur HTTP imprudent transforme directement le chemin dans l'URL en un nom de fichier du système de fichiers local, il peut sans s'en douter donner accès à des endroits non prévus. Par exemple, sur un serveur Unix, lorsque la requête est :

    GET /../../../../../../../../etc/passwd HTTP/1.1
    

    un serveur mal programmé donnerait accès au fichier (normalement non distribué /etc/passwd), car .., sur Unix, désigne le répertoire situé un cran au dessus (et le répertoire courant, si c'est la racine, donc l'attaquant a intérêt à mettre beaucoup de .. pour être sûr d'atteindre la racine avant de redescendre vers /etc).

    8.5. Attaque par injection de commandes ou de code

    Autre attaque possible, l'injection de commandes ou de code. Le contenu du chemin dans l'URL, ou celui des autres paramètres de la requête, ne mérite aucune confiance : il est complètement sous le contrôle du client, qui peut être un attaquant, et qui peut donc inclure des caractères spéciaux, interprétés par un des logiciels qui manipulent ce contenu. Imaginons par exemple que le contenu de l'en-tête Referer: soit mis dans une base de données relationnelle et que le client ait envoyé un en-tête :

    Referer: http://www.google.com/' ; DROP TABLE Statistics; SELECT'
    

    Comme l'apostrophe) et le point-virgule sont des caractères spéciaux pour le langage SQL, on pourrait réussir ici une injection SQL : le code SQL situé entre les deux apostrophes (ici, une destruction de table) sera exécuté. Ces attaques par injection sont bien connues, relativement faciles à empêcher (les données issues de l'extérieur ne doivent pas être passées à un autre logiciel avant désinfection), mais encore fréquentes.

    8.6. Vie privée

    HTTP soulève aussi plein de questions liées à la vie privée. On sait que le journal) d'un serveur HTTP peut révéler beaucoup de choses. Un serveur cache d'un réseau local, notamment, voit tout le trafic et peut le relier à des utilisateurs individuels. Bref, il faut traiter les journaux sérieusement : ils sont souvent soumis à des lois de protection de la vie privée (ils contiennent des informations qui sont souvent nominatives comme l'adresse IP du client HTTP), et ils doivent donc être gérés en accord avec les bonnes pratiques de sécurité (par exemple, lisibles seulement par les administrateurs système). Le RFC7231 recommande qu'on ne journalise pas tout ou que, si on le fait, on « nettoie » les journaux au bout d'un moment (par exemple en retirant l'adresse IP du client ou, tout simplement, en supprimant le journal).

    Un client HTTP peut envoyer plein d'informations révélatrices (comme la localisation physique de l'utilisateur, son adresse de courrier électronique, des mots de passe...) Le logiciel, qui connait ces informations, doit donc faire attention à ne pas les divulguer inutilement.

    Certaines personnes utilisent l'URI comme un mot de passe (en y incluant des données secrètes et en comptant que l'URI ne sera pas publié) ce qui est une très mauvaise idée. En effet, les URI sont partagés, par les systèmes de synchronisation de signets, par les navigateurs qui consultent des listes noires d'URI, par des utilisateurs qui n'étaient pas conscients que c'était un secret, par l'en-tête Referer:...

    Bref, il ne faut pas compter sur le secret de l'URI. Créer un site Web confidentiel et compter sur le fait qu'on n'a envoyé l'URI qu'à un petit groupe restreint de personnes est une très mauvaise stratégie de sécurité.

    Autre piège pour la vie privée, les informations apparemment purement techniques et non personnelles transmises par le navigateur Web, comme le User-Agent:, les en-têtes de négociation de contenu (comme Accept-Language:), mais aussi la liste des polices ou bien d'autres caractéristiques.

    Prises ensemble, ces informations permettent le fingerprinting, l'identification d'un navigateur unique au milieu de millions d'autres, grâce à ses caractéristiques uniques. Le fingerprinting marche bien car, en pratique, la combinaison de toutes ces informations techniques est souvent unique. Vous ne me croyez pas ? Regardez le Panopticlick.

results matching ""

    No results matching ""