Envoyer et extraire les données des formulaires

Cette traduction est incomplète. Aidez à traduire cet article depuis l'anglais.

La plupart du temps, un Formulaire HTML sert à envoyer des données à un serveur. Le serveur utilise les données et renvoie une réponse à l'utilisateur. Cela peut sembler simple mais il est important de garder certaines choses à l'esprit pour s'assurer que les données n'endommagent pas notre serveur ou ne causent pas de problèmes à nos utilisateurs.

Où vont les données ?

A propos de l'architecture client / serveur

Internet est fondé sur une architecture client / serveur très basique, qui peut être résumée ainsi : 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 un 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 "user-friendly" de configurer une requête HTTP pour envoyer des informations à un serveur. Cela permet à l'utilisateur de donner des informations qui seront délivrées dans la requête HTTP.

Côté cilent : définir comment envoyer les données

L'élément  <form> définit comment les données seront envoyées. Tous ses attributs nous laissent configurer la requête à envoyer lorsqu'un utilisateur clique sur 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. Si cet attribut n'est pas fourni, les données seront envoyées à l'URL de la page qui contient le formulaire.

Exemples

Dans cet exemple, les données sont envoyées à l'adresse http://foo.com :

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

Ici, les données sont envoyées au même serveur que celui qui héberge la page contenant le formulaire, mais à une autre URL du serveur :

<form action="/somewhere_else">

Lorsqu'il est spécifié sans attribut, comme ci-dessous, l'attribut <form> envoie les données sur la page qui contient le 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. Ce n'est plus le cas et 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 encrypté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 renvoient un message d'alerte à l'utilisateur chaque fois qu'il envoie des données car celles-ci ne sont pas crypté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.

Exemple

Considérons le formulaire suivant :

<form action="http://foo.com" method="get">
  <input name="say" value="Hi">
  <input name="to" value="Mom">
  <button>Send my greetings</button>
</form>

Avec la méthode GET, la requête HTTP ressemble à cela :

GET /?say=Hi&to=Mom HTTP/1.1
Host: foo.com
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 qui prend en compte les données contenues dans le corps de la requête HTTP : "Hé le serveur, regarde 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.

Exemple

Considérons le formulaire suivant (le même que ci-dessus) :

<form action="http://foo.com" method="post">
  <input name="say" value="Hi">
  <input name="to" value="Mom">
  <button>Send my greetings</button>
</form>

Si la requête HTTP est envoyée avec la méthode POST, la requête HTTP ressemble à cela :

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.

Bien sûr, 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. La seule chose visible pour l'utilisateur est l'URL appelée. Donc avec une requête GET, l'utilisateur verra les données dans la barre de navogation, mais avec une requête POST, il ne les verra pas. Cela peut être très important pour deux raisons :

  1. Si vous avez besoin d'envoyer un mot de passe (ou toute forme de données sensibles), n'utilisez jamais la méthode GET ou vous risquerez de le montrer dans la barre de navigation.
  2. Si vous devez envoyer une grande quantité de données, la méthode POST est préférable car certains navigateurs limitent les tailles des URL. En outre, de nombreux serveurs limitent les tailles des URL acceptées.

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 liste de paires clé / valeur. La façon d'accéder à cette liste dépend de la plate-forme de dévelppeur utilisée et des frameworks qu'on peut utiliser avec. La technologie utilisée détermine aussi comment les clés dupliquées sont gérées, la priorité étant donnée à la valeur la plus récente d'une clé.

Exemple: PHP

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

<?php
  // The global $_POST variable allows you to access the data sent with the POST method
  // To access the data sent with the GET method, you can use $_GET
  $say = htmlspecialchars($_POST['say']);
  $to  = htmlspecialchars($_POST['to']);

  echo  $say, ' ', $to;

Cet exemple affiche une page avec les données envoyées. Dans l'exemple précéder, le rendu serait :

Hi Mom

Exemple: Python

Cet exemple utilise Python pour faire la même chose : montrer les données sur une page web. Il utilise le Package Python CGI pour accéder aux données du formulaire.

#!/usr/bin/env python
import html
import cgi
import cgitb; cgitb.enable()     # for troubleshooting

print("Content-Type: text/html") # HTTP header to say HTML is following
print()                          # blank line, end of headers

form = cgi.FieldStorage()
say  = html.escape(form["say"].value);
to   = html.escape(form["to"].value);

print(say, " ", to)

Le résultat est le même qu'avec PHP.

Hi Mom

Autres langages et frameworks

Il y a de nombreux autres technologies orientées serveur qu'on peut utiliser pour gérer des formulaires, comme Perl, Java, .Net, Ruby... Prenez juste votre préféré. Cela dit, il faut noter qu'il n'est pas commun d'utiliser ces technologies directement car ça peut être délicat. Il est plus fréquent d'utiliser l'un des jolis frameworkds qui permettent de gérer des formulaires plus facilement, comme :

Enfin il faut noter que même en utilisant ces frameworks, travailler avec des formulaires n'est pas toujours facile. Mais c'est quand même bien mieux et cela vous économisera pas mal de temps.

Cas particulier : Envoyer des fichiers

Les fichiers sont un cas un peu spécial des formulaires HTML. Ce sont des données binaires - ou considérées telles quelles - où toutes les données sont du texte. Comme HTTP est un protocole de texte, il y a certaines conditions à remplir pour gérer des données binaires.

L'attribut enctype

Cet attribut vous permet de spécifier la valeur de l'en-tête HTTP Content-Type. Cet en-tête est très important car il dit au serveur quel type de données est envoyé. Par défaut, sa valeur est application/x-www-form-urlencoded. Ce qui signifie : "Ce sont des données issues d'un formulaire qui ont été encodées dans un formulaire URL."

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

  • Réglet l'attribut method à POST car le contenu du fichier ne peut pas être mis dans les paramètres d'une URL en utilisant un formulaire.
  • Régler la valeur de enctype à multipart/form-data car les données seront dissociées en de multiples parts, une pour chaque fichier et une pour le texte du corps du formulaire qui peut être envoyé avec eux.

Par exemple :

<form method="post" enctype="multipart/form-data">
  <input type="file" name="myFile">
  <button>Send the file</button>
</form>

Note : Certains navigateurs supportent l'attribut multiple de l'élément <input>pour envoyer plus d'un fichier avec seulement un élément d'entrée. Comment le serveur gère ces fichiers dépend de la technologie du serveur. Comme évoqué précédemment, utiliser un framework 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.

Questions relatives à la 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 contre les serveurs. Les problèmes ne viennent jamais des formulaires eux-mêmes mais de la façon dont les serveurs gèrent les données.

Failles de sécurité courantes

Voici quelques failles de sécurité bien connues :

XSS et CSRF

Les attaques Cross-Site Scripting (XSS) et Cross-Site Request Forgery (CSRF) sont des attaques fréquentes qui arrivent quand on renvoie les données envoyées par un utilisateur à celui-ci ou à un autre utilisateur. 

XSS laisse les attaqueurs injecter des scripts orientés clients dans les pages Web vues par les autres utilisateurs. La vulnérabilité d'un site peut être utilisée par les attaquants pour contourner les contrôles d'accès comme la politique de la même origine. Les effets de ces attaques peuvent aller d'une nuisance insignifiante à 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 orientés clients sur des pages Web - mais leur cible est différente. Les attaquants CSRF essaient d'escalade les 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 qui n'a pas les mêmes droits.

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 devez les afficher) essayer de ne pas afficher le contenu HTML tel que fourni par l'utilisateur. A la place, vous devriez traiter les données fournies par l'utilisateur afin de ne pas afficher le verbatim. La quasi totalité des frameworks sur le marché implémentent un filtre minimum qui enlèvent les éléments <script>, <iframe> and <object> des données envoyées par les utilisateurs. Cela permet d'atténuer les risques sans toutefois les éradiquer.

L'injection SQL

L'injection SQL est un type d'attaque qui essaie de commettre des actions dans des bases de données utilisées par le site web cible. Cela implique généralement d'envoyer une requête SQL en espérant que le serveur l'exécutera (plusieurs fois quand le serveur essaie de ranger les informations). C'est 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'escalade des privilèges. C'est une menace sérieuse et vous ne devriez jamais ranger des données envoyées par un utilisateur sans faire de l'aseptisation (par exemple en utilisant mysql_real_escape_string() sur une infrastructure PHP/MySQL).

L'injection d'en-tête HTTP et l'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 de hameçonnage (phishing).

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

Soyez parano : Ne faites jamais confiances à vos utilisateurs

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

Toutes les données qui vont à votre serveur doivent être vérifiées et nettoyées. Toujours. Sans exception.

  • Escape potentially dangerous characters. The specific characters you should be cautious with vary depending on the context in which the data is used and the server platform you employ, but all server-side languages have functions for this.
  • Limit the incoming amount of data to allow only what's necessary.
  • Sandbox uploaded files (store them on a different server and allow access to the file only through a different subdomain or even better through a fully different domain name).

You should avoid many/most problems if you follow these three rules, but it's always a good idea to get a security review performed by a competent third party. Don't assume that you've seen all the possible problems.

Conclusion

As you can see, sending form data is easy, but securing an application can be tricky. Just remember that a front-end developer is not the one who should define the security model of the data. Yes, as we'll see, it's possible to perform client side data validation but the server can't trust this validation because it has no way to truly know what really happens on the client side.

See also

If you want to learn more about securing a web application, you can dig into these resources:

Étiquettes et contributeurs liés au document

 Contributeurs à cette page : teoli, rebeccachaix
 Dernière mise à jour par : rebeccachaix,