PHP / « Bien » contrôler les données que vous recevez (GET, POST, Cookies…)

Avant de commencer cet article, certains me diront sûrement que mon titre possède 3 lettres de trop, ou « Comment as-tu osé refaire du PHP ?! »… Et pourtant…
Pour mon cas, il s’agissait de reprendre en main un webservice PHP générant du XML et de l’optimiser.

Ce que je veux montrer aujourd’hui concerne un point essentiel que l’on m’a souvent répété lorsque j’étais étudiant qui est de ne jamais faire confiance à ce que saisit l’utilisateur, surtout lorsque l’on fait du web ou du client/serveur (le fameux « Never Trust User Input »).

Si on permet à l'utilisateur de saisir n'importe quoi, on peut se retrouver avec quelques surprises...

Si on permet à l’utilisateur de saisir n’importe quoi, on peut se retrouver avec quelques surprises…

En effet, notre utilisateur en question est tout à fait capable de nous envoyer:

  • une adresse email au format incorrect (sans le @ et le point)
  • des lettres dans un numéro de téléphone
  • pire encore, du code afin de pratiquer une injection SQL

Bref, j’en passe…

L’ancienne méthode:

Pour récupérer nos valeurs avec PHP, il suffisait de passer par les variables $_GET et $_POST:

$id = $_GET['id'];
$email = $_POST['email'];
$telephone = $_POST['telephone'];

Seulement, il arrivait parfois de recevoir un message d’erreur de ce genre: Notice: Undefined index si l’un de nos champs n’était pas renseigné. (en fonction de la version et de la configuration PHP)
Pour éviter cela, on utilisait les fonctions isset() et empty():

// Id
$if(isset($_GET['id']) && !empty($_GET['id']))
   $id = $_GET['id'];
else
   $error = "Aucun identifiant spécifié";

// Email
if(isset($_POST['email']) && !empty($_POST['email']))
   $email = $_POST['email'];
else
   $error = "Veuillez indiquer votre adresse email";

// Téléphone
if(isset($_POST['telephone']) && !empty($_POST['telephone']))
   $telephone = $_POST['telephone'];
else
   $error = "Veuillez indiquer votre numéro de téléphone";

On a récupéré nos valeurs.

Chouette !

TrollFaceDancing
Mais est-ce que ces données sont correctes et pertinentes ?
Est-ce qu’il n’y a pas une tentative d’injection SQL ou HTML derrière ?
Pour le savoir, il fallait tout un ensemble de lignes qui pouvait devenir assez lourd…

$id = trim(mysql_real_escape_string(htmlentities(strip_tags($id, ENT_QUOTES))));
$nom = trim(mysql_real_escape_string(htmlentities(strip_tags($nom, ENT_QUOTES))));
$telephone = trim(mysql_real_escape_string(htmlentities(strip_tags($telephone, ENT_QUOTES))));

Bref, ça fait pas mal de choses et ça peut devenir vite compliqué à gérer…

Et pour couronner le tout…

Désormais, PHP déconseille fortement désormais de passer directement par les variables $_GET, $_POST, $_COOKIE etc.

Et là c'est le drame...

Et là c’est le drame…

La nouvelle méthode:

Seulement, PHP a à la fois voulu compliquer et simplifier les choses. Il y a plus à écrire pour récupérer notre valeur mais il nous est désormais possible de la filtrer ou de la vérifier directement.
Désormais, on utilise la fonction filter_input apparue avec PHP 5.2. Cette fonction permet de récupérer nos valeurs en y appliquant un filtre de validation ou de nettoyage.

  • Voici un exemple avec un filtre de validation:
    $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL); 
    • Si le champs email n’est pas défini, $email sera NULL.
    • Si le contenu d’email n’est pas une adresse email, $email vaudra FALSE.
  • Et un autre avec un filtre de nettoyage:
    $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
    • Si le paramètre id n’est pas défini, $id sera NULL.
    • S’il l’est, $id ne conservera que les chiffres.
Pas trop compliqué finalement ?

Pas trop compliqué finalement ? 🙂

Comment appeler filter_input()

  • En premier paramètre, vous indiquez la provenance de votre donnée:
    • INPUT_GET: cherche dans $_GET
    • INPUT_POST: cherche dans $_POST
    • INPUT_COOKIE: cherche dans $_COOKIE
    • INPUT_SERVER: cherche dans $_SERVER
    • INPUT_ENV: cherche dans $_ENV (variables d’environnement)
  • En second paramètre, on indique le nom du champs que nous souhaitons récupérer.
  • En troisième paramètre, on indique le filtre de validation ou de nettoyage que l’on souhaite indiquer. Ce filtre est facultatif.

    Des filtres, il y en a plein ! Comme par exemple FILTER_VALIDATE_BOOLEAN, FILTER_VALIDATE_EMAIL, FILTER_VALIDATE_URL, FILTER_VALIDATE_INT, FILTER_SANITIZE_STRING, FILTER_SANITIZE_URL

  • En quatrième paramètre, on peut indiquer des attributs supplémentaires en fonction du filtre que l’on utilise. Par exemple, on peut dire que notre filtre doit absolument récupérer une valeur entre 0 et 1000.

Ces filtres sont vraiment pratiques non ?

Facile...

Facile…

Re-voici le prototype de la fonction:

mixed filter_input(int $type, string $variable_name [, int $filter = FILTER_DEFAULT [, mixed $options ]])

Retenez juste que pour les filtres de validation:

  • Si le champs n’est pas défini, la fonction retourne NULL.
  • Si le contenu n’est pas en adéquation avec le filtre, la fonction retourne FALSE.

Et pour les filtres de nettoyage:

  • Si le champs n’est pas défini, la fonction retourne NULL.
  • Si le champs est défini, la chaîne est retournée après nettoyage.

A vous de faire les contrôles nécessaires afin de sécuriser au mieux et facilement votre web-service / back-end 😉

Ressources:

filter_input sur PHPManual

Articles similaires:

Lien Permanent pour cet article : https://www.jbvigneron.fr/2014/05/15/php-controler-donnees-get-post-cookies/

(2 commentaires)

  1. Je ne connaissais pas cette nouvelle fonction.

    De plus en plus de gens font appel à des frameworks de développement (ZEND, CakePHP, Symofony…) pour développer leur site.

    Comme tu le dis, en général, on revient à utiliser ces fonctions uniquement dans le cadre d’une reprise de projet (j’en ai eu une avec un client…) dans ce cas, on préférera continuer à utiliser ces fonctions…
    Sinon, on s’orientera vers un framework permettant la validation des données, empêcher les injections… (MVC…)

    ça peut être utile, mais ça reste utile à une poignée de personne !

    Merci néanmoins pour ce très bon tutoriel !

    • Nico on 26 janvier 2016 at 10 h 22 min
    • Répondre

    MVC n’est pas franchement fait pour vérifier les injections de donnée, mais je comprend l’idée 🙂 En revanche, est-ce qu’il est possible de développer ses propres filtres? – Je pourrais regarder la doc, mais ça ferait aussi l’objet d’un article aussi intéressant que celui-ci 😉 –

Laisser un commentaire

Your email address will not be published.

css.php