We're looking for a user researcher to understand the needs of developers and designers. Is this you or someone you know? Check out the post: https://mzl.la/2IGzdXS

Cet article examine ce qui arrive quand un utilisateur soumet un formulaire — où les données vont-elles et comment les gère-t-on une fois à destination ? Nous examinerons aussi quelques problèmes de sécurité associés à l'envoi des données d'un formulaire.

Prérequis : Notions concernant les ordinateurs, compréhension du HTML, et connaissances de base de HTTP et programmation côté serveur.
Objectif : Comprendre ce qui arrive quand les données d'un formulaire sont soumises, y compris les notions de la façon dont les données sont traitées sur le serveur.

Où vont les données ?

Dans ce paragraphe, nous expliquons ce qui arrive aux données lors de la soumission d'un formulaire.

A propos de l'architecture client / serveur

Le web se fonde sur une architecture client/serveur élémentaire ; en résumé : un client (généralement un navigateur Web) envoie une requête à un serveur (le plus souvent un serveur web comme Apache, Nginx, IIS, Tomcat...), en utilisant le protocole HTTP. Le serveur répond à la requête en utilisant le même protocole.

A basic schema of the Web client/server architecture

Côté client, un formulaire HTML n'est rien d'autre qu'un moyen commode et convivial de configurer une requête HTTP pour envoyer des données à un serveur. L'utilisateur peut ainsi adresser des informations à joindre à la requête HTTP.

Note: Pour une meilleure idée du fonctionnement de l'architecture client‑serveur, lisez notre module Programmation d'un site web côté‑serveur : premiers pas.

Côté client : définition de la méthode d'envoi des données

L'élément  <form> définit la méthode d'envoi des données. Tous ses attributs sont conçus pour vous permettre de configurer la requête à envoyer quand un utilisateur presse le bouton d'envoi. Les deux attributs les plus importants sont action et method.

L'attribut action

Cet attribut définit où les données sont envoyées. Sa valeur doit être une URL valide. S'il n'est pas fourni, les données seront envoyées à l'URL de la page contenant le formulaire.

Dans cet exemple, les données sont envoyées à une URL précise — http://foo.com :

<form action="http://foo.com">

Ici, nous utilisons une URL relative — les données sont envoyées à une URL différente sur le serveur :

<form action="/somewhere_else">

Sans attribut, comme ci-dessous, les données de <form> sont envoyées à la même page que celle du formulaire  :

<form>

De nombreuses pages anciennes utilisent la notation suivante pour indiquer que les données doivent être envoyées à la page qui contient le formulaire. C'était nécessaire car jusqu'à HTML5, l'attribut action était requis. Il n'y en a donc plus besoin.

<form action="#">

Note : Il est possible de spécifier une URL qui utilise le protocole HTTPS (HTTP sécurisé). Quand vous faites ceci, les données sont chiffrées avec le reste de la requête, même si le formulaire lui-même est hébergé dans une page non sécurisée à laquelle on accède via HTTP. D'autre part, si le formulaire est hébergé sur une page sécurisée mais qu'on spécifique une URL non sécurisée avec l'attribut action, tous les navigateurs affichent une alerte de sécurité pour l'utilisateur chaque fois qu'il envoie des données car celles-ci ne sont pas chiffrées.

L'attribut method

Cet attribut définit comment les données sont envoyées. Le Protocole HTTP fournit plusieurs façons d'envoyer une requête. Les données des formulaires HTML peuvent être envoyées via au moins deux méthodes : la méthode GET et la méthode POST.

Pour bien comprendre la différence entre ces deux méthodes, il convient d'examiner comment le protocole HTTP marche. Chaque fois qu'on veut atteindre une ressource sur Internet, le navigateur envoie une requête à une URL. Une requête HTTP consiste en deux parties : un en-tête (header) qui contient les métadonnées sur les capacités du navigateur, et un corps (body) qui contient les informations nécessaires au serveur pour effectuer la requête.

La méthode GET

La méthode GET est utilisée par le navigateur pour demander au serveur de renvoyer une certaine ressource. "Hé le serveur, je veux cette ressource." Dans ce cas, le navigateur envoie un corps vide. Du coup, si un formulaire est envoyé avec cette méthode, les données envoyées au serveur sont ajoutées à l'URL.

Considérons le formulaire suivant :

<form action="http://foo.com" method="get">
  <div>
    <label for="say">Quel salut voulez-vous adresser ?</label>
    <input name="say" id="say" value="Hi">
  </div>
  <div>
    <label for="to">À qui voulez‑vous l'adresser ?</label>
    <input name="to" value="Mom">
  </div>
  <div>
    <button>Send my greetings</button>
  </div>
</form>

Comme nous avons utilisé la méthode GET, vous verrez l'URL www.foo.com/?say=Hi&to=Mom apparaître dans la barre du navigateur quand vous soumettez le formulaire.

Les données sont ajoutées à l'URL sous forme d'une suite de paires nom/valeur. À la fin de l'URL de l'adresse Web, il y a un point d'interrogation (?) suivi par les paires nom/valeur séparés par une esperluette (&). Dans ce cas, nous passons deux éléments de données au serveur :

  • say, dont la valeur est  Hi
  • to, qui a la valeur Mom

La requête HTTP ressemble à quelque chose comme :

GET /?say=Hi&to=Mom HTTP/1.1
Host: foo.com

Note : Vous trouverez cet exemple sur GitHub — voyez get-method.html (à voir directement aussi).

La méthode POST

La méthode POST est un peu différente.C'est la méthode que le navigateur utilise pour demander au serveur une réponse prenant en compte les données contenues dans le corps de la requête HTTP : « Hé serveur ! vois ces données et renvoie-moi le résultat approprié ». Si un formulaire est envoyé avec cette méthode, les données sont ajoutées au corps de la requête HTTP.

Voyons un exemple — c'est le même formulaire que celui que nous avons vu pour GET ci‑dessus, mais avec post comme valeur de l'attribut  method.

<form action="http://foo.com" method="post">
  <div>
    <label for="say">Quel salut voulez‑vous adresser ?</label>
    <input name="say" id="say" value="Hi">
  </div>
  <div>
    <label for="to">À qui voulez‑vous l'adresser ?</label>
    <input name="to" value="Mom">
  </div>
  <div>
    <button>Send my greetings</button>
  </div>
</form>

Quand le formulaire est soumis avec la méthode POST, aucune donnée n'est ajoutée à l'URL et la requête HTTP ressemble à ceci, les données incorporées au corps de requête :

POST / HTTP/1.1
Host: foo.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 13

say=Hi&to=Mom

L'en-tête Content-Length indique la taille du corps, et l'en-tête Content-Type indique le type de ressources envoyées au serveur. Nous discuterons de ces en-têtes dans un moment.

Note : Vous trouverez cet exemple sur GitHub — voyez post-method.html (à voir directement aussi).

Voir les requêtes HTTP

Les requêtes HTTP ne sont jamais montrées à l'utilisateur (si vous voulez les voir, vous devez utiliser des outils comme la Console Web de Firefox ou les Chrome Developer Tools). À titre d'exemple, les données de formulaire sont visibles comme suit dans l'onglet Chrome Network.  Après avoir soumis le formulaire :

  1. Pressez F12
  2. Selectionnez « Réseau »
  3. Selectionnez « Tout »
  4. Selectionnez « foo.com » dans l'onglet « Nom »
  5. Selectionnez « En‑tête »

Vous obtiendrez les données du formulaire, comme l'image suivante le montre.

La seule chose affichée à l'utilisateur est l'URL appelée. Comme mentionné ci‑dessus, avec une requête GET l'utilisateur verra les données dans la barre de l'URL, mais avec une requête POST il ne verra rien. Cela peut être important pour deux raisons :

  1. Si vous avez besoin d'envoyer un mot de passe (ou toute autre donnée sensible), n'utilisez jamais la méthode GET ou vous risquez de l'afficher dans la barre d'URL, ce qui serait très peu sûr.

  2. Si vous avez besoin d'envoyer une grande quantité de données, la méthode POST est préférable car certains navigateurs limitent la taille des URLs. De plus, de nombreux serveurs limitent la longueur des URL qu'ils acceptent.

Côté serveur : récupérer les données

Quelle que soit la méthode HTTP qu'on choisit, le serveur reçoit une chaîne de caractères qui sera décomposée pour récupérer les données comme une liste de paires clé/valeur. La façon d'accéder à cette liste dépend de la plateforme de développement utilisée et des modèles qu'on peut utiliser avec. La technologie utilisée détermine aussi comment les clés dupliquées sont gérées ; souvent, la priorité est donnée à la valeur de clé la plus récente.

Exemple : PHP brut

Le PHP met à disposition des objets globaux pour accéder aux données. En supposant que vous avez utilisé la méthode POST, l'exemple suivant récupère les données et les affiche à l'utilisateur. Bien sûr, ce que vous en faites dépend de vous. Vous pouvez les afficher, les ranger dans une base de données, les envoyer par mail ou les traiter autrement.

<?php
  // La variable globale $_POST vous donne accès aux données envoyées avec la méthode POST par leur nom
  // Pour avoir accès aux données envoyées avec la méthode GET, utilisez $_GET
  $say = htmlspecialchars($_POST['say']);
  $to  = htmlspecialchars($_POST['to']);

  echo  $say, ' ', $to;

Cet exemple affiche une page avec les données envoyées. Vous pouvez voir ceci opérer avec notre fichier exemple php-example.html — il contient le même formulaire exemple que celui vu précédemment avec la méthode post avec php-example.php en action. À la soumission du formulaire, il envoie les données de ce dernier à php-example.php, contenant le code ci‑dessus. Quand le code est exécuté, la sortie dans le navigateur est Hi Mom.

Note : Cet exemple ne fonctionnera pas si vous le chargez localement dans un navigateur — les navigateurs ne savent pas interpréter le code PHP, donc quand le formulaire est soumis, le navigateur vous offrira seulement de télécharger le fichier PHP pour vous. Pour qu'il s'exécute, il est nécessaire de lancer l'exemple par l'intermédiaire d'un serveur PHP de n'importe quel type. Les bons choix pour des tests locaux de PHP sont MAMP (Mac et Windows) et AMPPS (Mac, Windows, Linux).

Exemple: Python

Cet exemple vous montre comment utiliser Python pour faire la même chose — afficher les données sur une page web. Celui‑ci utilise Flask framework pour le rendu des modèles, la gestion de la soumission des données du formulaire, etc (voyez python-example.py).

from flask import Flask, render_template, request
app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def form():
    return render_template('form.html')

@app.route('/hello', methods=['GET', 'POST'])
def hello():
    return render_template('greeting.html', say=request.form['say'], to=request.form['to'])

if __name__ == "__main__":
    app.run()

Les deux prototypes  référencés dans le code ci‑dessus sont les suivants :

  • form.html : Le même formulaire que celui vu plus haut dans la section La méthode POST mais avec l'attribut action défini à la valeur {{url_for('hello')}}. (C'est un modèle Jinja2, qui est HTML à la base mais peut contenir des appels à du code Python qui fait tourner le serveur web mis entre accolades. url_for('hello') dit en gros  « à rediriger sur /hello quand le formulaire est soumis ».)
  • greeting.html : Ce modèle contient juste une ligne qui renvoie les deux éléments de donnée qui lui sont passées lors du rendu. Cela est effectué par l'intermédiaire de la fonction hello() vue plus haut qui s'exécute quand l'URL /hello est chargée dans le navigateur.

Note : À nouveau, ce code ne fonctionnera pas si vous tentez de le charger directement dans le navigateur. Python fonctionne un peu différemment de PHP — pour exécuter ce code localement il est nécessaire d'installer Python/PIP, puis Flask avec « pip3 install flask ». À ce moment‑là vous pourrez exécuter l'exemple avec « python3 python-example.py », puis en allant sur « localhost:5000 » dans votre navigateur.

Autres langages et canevas de structures

Il y a de nombreuses autres techniques côté serveur utilisables pour gérer des formulaires, comme Perl, Java, .Net, Ruby... retenez juste votre préférée. Cela dit, il faut noter qu'il n'est pas très courant d'utiliser ces techniques directement car cela peut être délicat. Il est plus fréquent d'utiliser l'un des jolis canevas qui permettent de gérer des formulaires plus facilement, comme :

Enfin il faut noter que même en utilisant ces canevas, travailler avec des formulaires n'est pas toujours facile. Mais c'est quand même bien plus facile que d'essayer d'en écrire vous‑même les fonctionnalités et cela vous économisera pas mal de temps.

Note : Nous déborderions du cadre de cet article en vous initiant aux langages côté serveur ou aux canevas. Les liens ci‑dessus vous donneront des informations si vous souhaitez en apprendre plus.

Cas particulier : envoyer des fichiers

L'envoi de fichiers avec une formulaire HTML est cas particulier. Les fichiers sont des données binaires — ou considérées comme telles — alors que toutes les autres données sont des données textuelles. Comme HTTP est un protocole de texte, il y a certaines conditions particulières à remplir pour gérer des données binaires.

L'attribut enctype

Cet attribut vous permet de préciser la valeur de l'en-tête HTTP Content-Type incorporé dans la requête générée au moment de la soumission du formulaire. Cet en-tête est très important car il indique au serveur le type de données envoyées. Par défaut, sa valeur est application/x-www-form-urlencoded. Ce qui signifie : « Ce sont des données de formulaire encodées à l'aide de paramètres URL ».

Mais si vous voulez envoyer des fichiers, il faut faire deux choses en plus :

  • régler l'attribut method à POST car un contenu de fichier ne peut pas être mis dans des paramètres d'URL.
  • régler la valeur de enctype à multipart/form-data car les données seront coupées en plusieurs parties, une pour chaque fichier plus une pour les données dans le corps du formulaire (si du texte a aussi été entré dans le formulaire).
  • incorporer un ou plusieurs widgets de sélection de fichiers pour permettre aux utilisateurs de choisir les fichiers à téléverser.

 Par exemple :

<form method="post" enctype="multipart/form-data">
  <div>
    <label for="file">Choose a file</label>
    <input type="file" id="file" name="myFile">
  </div>
  <div>
    <button>Send the file</button>
  </div>
</form>

Note : Certains navigateurs prennent en charge l'attribut multiple de l'élément <input>pour envoyer plus d'un fichier avec seulement un élément d'entrée. La façon dont le serveur gère ces fichiers dépend de la technologie du serveur. Comme évoqué précédemment, utiliser un modèle rendra les choses plus faciles.

Attention : De nombreux serveurs sont configurés avec une limite de taille pour les fichiers et les requêtes HTTP pour éviter les abus. Il est important de vérifier cette limite avec l'administrateur du serveur avant d'envoyer un fichier.

Problèmes courants de sécurité

Chaque fois qu'on envoie des données à un serveur, il faut considérer la sécurité. Les formulaires HTML sont l'un des principaux vecteurs d'attaque (emplacements d'où les attaques peuvent provenir) contre les serveurs. Les problèmes ne viennent jamais des formulaires eux-mêmes — ils proviennent de la façon dont les serveurs gèrent les données.

Selon ce que vous faites, il y a quelques problèmes de sécurité bien connus que vous pouvez aisément contrer :

XSS et CSRF

Les attaques Cross-Site Scripting (XSS) et Cross-Site Request Forgery (CSRF) sont des attaques fréquentes qui surviennent quand vous affichez des données renvoyées par un utilisateur à celui-ci ou à un autre utilisateur. 

XSS permet aux attaquants d'injecter des scripts  côté‑client dans les pages Web vues par d'autres utilisateurs. La vulnérabilité au XSS peut être utilisée par les attaquants pour contourner les contrôles d'accès comme la same-origin policy (ou politique de même origine). Les effets de ces attaques peuvent aller d'une nuisance mineure à un risque significatif de sécurité.

Les attaques CSRF sont similaires aux attaques XSS en ce qu'elles commencent de la même façon — en injectant des scripts côté‑client sur des pages Web — mais leur cible est différente. Les attaquants CSRF essaient d'accroître leurs privilèges pour atteindre ceux d'un utilisateur privilégié (par exemple l'administrateur du site) pour effectuer une action qu'ils ne devraient pas pouvoir faire (par exemple, envoyer des données à un utilisateur non habilité).

Les attaques XSS exploitent la confiance qu'un utilisateur a pour un site, alors que les attaques  CSRF exploitent la confiance qu'un site a pour ses utilisateurs.

Pour éviter ces attaques, vous devez toujours vérifier les données qu'un utilisateur envoie à votre serveur et (si vous avez besoin de les afficher) essayez de ne pas afficher le contenu HTML tel que fourni par l'utilisateur. A la place, vous devez traiter les données fournies par l'utilisateur afin de ne pas les afficher verbatim. La quasi totalité des modèles du marché implémentent un filtre minimum qui enlève les éléments <script>, <iframe> et <object> des données envoyées par les utilisateurs. Cela permet d'atténuer les risques sans toutefois les éradiquer.

Injection SQL

L'injection SQL est un type d'attaque qui essaie d'effectuer certaines actions sur les bases de données utilisées par le site web cible. Cela consiste d'ordinaire à envoyer une requête SQL en espérant que le serveur l'exécutera (généralement quand le serveur essaie de ranger les informations envoyées par un utilisateur). C'est assurément l'un des vecteurs d'attaque les plus fréquents contre les sites web.

Les conséquences peuvent être terribles, de la perte de données à l'accès à l'infrastructure en utilisant l'augmentation des privilèges. C'est une menace sérieuse et vous ne devez jamais ranger des données envoyées par un utilisateur sans asepsie préalable (par exemple en utilisant mysql_real_escape_string() sur une infrastructure PHP/MySQL).

Injection d'en-tête HTTP et injection d'email

Ces attaques peuvent arrivent quand votre application construit des en-têtes HTTP ou des emails basés sur les données entrées par un utilisateur sur un formulaire. Elles ne vont pas directement endommager votre serveur ou affecter vos utilisateurs mais elles sont la porte ouverte à des problèmes plus graves comme le piratage de session ou les attaques par hameçonnage (phishing).

Ces attaques sont généralement silencieuses et peuvent transformer votre serveur en zombie.

Soyez paranoïaque : ne faites jamais confiances à vos utilisateurs

Alors, comment combattre ces menaces ? Ce sujet va bien au-delà de ce guide mais il y a quelques règles à garder en tête. La principale est de ne jamais faire confiance à ses utilisateurs, vous-même compris : même un utilisateur de confiance peut s'être fait pirater.

Toute donnée qui va dans un serveur doit être vérifiée et nettoyée. Toujours. Sans exception.

  • Échappez les caractères putativement dangereux. Les caractères spécifiques avec lesquels vous devez être prudent varient selon le contexte dans lequel les données sont utilisées et de la plate-forme serveur que vous utilisez, mais tous les languages côté serveur ont des fonctions pour cela.
  • Limitez le volume des données entrantes pour n'autoriser que ce qui est nécessaire.
  • Faites passer par le bac à sable les fichiers téléversés  (stockez‑les sur un serveur différent et n'autorisez l'accès au fichier que dans un sous-domaine différent, ou mieux, par un nom de domaine complètement différent).

Vous devriez vous éviter beaucoup de problèmes en suivant ces trois règles, mais cela reste néanmoins une bonne idée de faire un examen de sécurité auprès d'une tierce personne compétente. Ne pensez pas, à tort, avoir anticipé tous les problèmes de sécurité !

Note : L'article « Sécurité des sites Web » de notre sujet d'apprentissage « côté‑serveur » discute les problèmes ci‑dessus et leurs solutions possibles plus en détail.

Conclusion

Comme vous pouvez le voir, envoyer un formulaire est facile, mais sécuriser son application peut s'avérer plus délicat. N'oubliez pas qu'un développeur n'est pas celui qui doit définir le modèle de sécurité des données. Comme nous allons le voir, il est possible d'effectuer la validation des données côté client, mais le serveur ne peut pas faire confiance à cette validation car il n'a aucun moyen de savoir ce qui se passe réellement du côté client.

Voir aussi

Si vous voulez en savoir plus par rapport aux applications web, vous pouvez consulter ces ressources :

Dans ce module

Étiquettes et contributeurs liés au document

Contributeurs à cette page : Dralyab, ChristianR25, Bam92, ben391, Thorium90, teoli, rebeccachaix
Dernière mise à jour par : Dralyab,