Les données reçues par une application PHP ne doivent jamais être considérées comme fiables par défaut. Un formulaire, un paramètre d’URL, un cookie ou une donnée issue d’une API peut contenir une valeur incorrecte, inattendue ou malveillante.

La validation permet de vérifier qu’une donnée respecte bien le format, le type et les règles attendues avant de l’utiliser. Cette étape réduit les erreurs et participe directement à la sécurité de l’application.

Quelles données doivent être contrôlées ?

Toutes les données provenant de l’extérieur de l’application doivent être vérifiées.

Cela concerne notamment :

  • les données reçues via $_POST ;
  • les paramètres transmis par $_GET ;
  • les cookies ;
  • les fichiers téléversés ;
  • les en-têtes HTTP ;
  • les paramètres d’URL ;
  • les données provenant d’une API ;
  • certaines données issues d’un autre service ou d’une source externe.

Il ne faut donc pas limiter la validation aux formulaires visibles par l’utilisateur.

Un attaquant peut modifier directement une requête HTTP, sans passer par l’interface prévue par le site. De plus, une donnée techniquement valide peut rester incorrecte au regard des règles métier de l’application.

Validation et nettoyage : quelle différence ?

La validation et le nettoyage ne répondent pas au même besoin.

Valider une donnée consiste à vérifier qu’elle correspond à ce que l’application attend.

Par exemple :

  • un identifiant doit être un entier positif ;
  • une adresse e-mail doit respecter un format valide ;
  • une date doit correspondre au format prévu ;
  • une valeur doit appartenir à une liste autorisée ;
  • un nombre doit rester dans une plage définie.

Nettoyer une donnée consiste à la transformer ou à retirer certains caractères.

Cette distinction est essentielle.

Lorsqu’une application attend une valeur précise, il vaut souvent mieux refuser une donnée invalide plutôt que tenter de la corriger automatiquement.

Par exemple, si un champ attend uniquement les valeurs admin, manager ou user, toute autre valeur doit être rejetée.

Chargement...

Utiliser les filtres de validation de PHP

PHP fournit plusieurs fonctions utiles pour valider les données, notamment filter_var() et filter_input().

Pour valider une adresse e-mail :

$email = filter_input(
    INPUT_POST,
    'email',
    FILTER_VALIDATE_EMAIL
);

Si la donnée correspond au format attendu, PHP retourne sa valeur. Sinon, il retourne false.

Il faut toutefois distinguer une valeur invalide d’une valeur absente. Selon le cas, filter_input() peut également retourner null.

PHP propose notamment les filtres suivants :

FILTER_VALIDATE_INT
FILTER_VALIDATE_FLOAT
FILTER_VALIDATE_BOOLEAN
FILTER_VALIDATE_EMAIL
FILTER_VALIDATE_URL
FILTER_VALIDATE_IP

Il faut toujours sélectionner explicitement le filtre adapté au type de donnée attendu.

Attention au filtre par défaut

Un piège fréquent concerne FILTER_DEFAULT.

Malgré son nom, ce filtre ne réalise pas de validation de sécurité. Il correspond à FILTER_UNSAFE_RAW.

Ainsi :

$value = filter_var($value);

ne protège pas automatiquement la donnée.

Il faut donc choisir un filtre précis lorsqu’une validation ou une transformation est nécessaire.

Ajouter des règles métier

La validation technique ne suffit pas toujours.

Une valeur peut être correcte du point de vue du langage, tout en étant incohérente pour l’application.

Prenons l’exemple d’un âge :

$age = filter_input(
    INPUT_POST,
    'age',
    FILTER_VALIDATE_INT,
    [
        'options' => [
            'min_range' => 18,
            'max_range' => 120
        ]
    ]
);

Ici, PHP vérifie que la valeur est un entier, mais aussi qu’elle respecte la plage autorisée.

Autre exemple avec une liste de rôles :

$rolesAutorises = ['user', 'manager', 'admin'];

if (!in_array($role, $rolesAutorises, true)) {
    throw new InvalidArgumentException('Rôle invalide');
}

Cette approche repose sur une liste d’autorisation, aussi appelée allowlist.

Elle est généralement plus sûre qu’une liste noire, car elle définit directement ce qui est accepté.

Ne plus utiliser FILTER_SANITIZE_STRING

Les anciens exemples PHP utilisent souvent FILTER_SANITIZE_STRING.

Cette pratique ne doit plus être recommandée.

Elle entretenait notamment une confusion entre deux sujets différents :

  • la validation des données à l’entrée ;
  • la protection contre les attaques XSS à l’affichage.

Pour protéger un affichage HTML, il faut encoder la donnée au moment où elle est affichée.

Par exemple :

echo htmlspecialchars(
    $nom,
    ENT_QUOTES | ENT_SUBSTITUTE,
    'UTF-8'
);

Il faut cependant adapter cette protection au contexte.

Une donnée insérée dans du HTML, un attribut, une URL ou du JavaScript ne se protège pas toujours de la même manière.

Valider les entrées ne suffit pas contre les injections SQL

La validation des données réduit certains risques, mais elle ne remplace pas les protections propres aux bases de données.

Une requête SQL ne doit jamais être construite en concaténant directement une donnée utilisateur.

À éviter :

$sql = "SELECT * FROM users WHERE email = '$email'";

Il faut utiliser des requêtes préparées :

$stmt = $pdo->prepare(
    'SELECT id, email FROM users WHERE email = :email'
);

$stmt->execute([
    'email' => $email
]);

Les requêtes préparées séparent la structure de la requête des données utilisées.

Elles constituent donc une protection essentielle contre les injections SQL.

Éviter l’échappement manuel des données SQL

Une ancienne approche consistait à échapper manuellement les caractères spéciaux avant d’intégrer la valeur dans la requête.

Cette méthode est plus fragile.

Même si PDO::quote() existe toujours, il ne doit pas devenir la stratégie principale pour sécuriser les requêtes.

Il vaut mieux utiliser systématiquement les requêtes préparées dès qu’une donnée dynamique intervient dans une requête.

Par exemple :

$stmt = $pdo->prepare(
    'INSERT INTO users (name, email)
     VALUES (:name, :email)'
);

$stmt->execute([
    'name' => $name,
    'email' => $email
]);

Cette méthode est plus claire, plus robuste et plus simple à maintenir.

Contrôler les fichiers téléversés

Les fichiers envoyés par un utilisateur présentent des risques particuliers.

Il ne suffit pas de vérifier leur extension.

Une application doit notamment contrôler :

  • la taille du fichier ;
  • le type de fichier détecté ;
  • le format attendu ;
  • le nom utilisé pour l’enregistrement ;
  • l’emplacement de stockage ;
  • les droits d’accès ;
  • les règles d’exécution du serveur.

Il faut également éviter de stocker directement des fichiers utilisateurs dans un dossier où le serveur web peut exécuter du code.

Selon le contexte, une analyse antivirus ou antimalware peut aussi être utile.

Contrôler les données avant leur utilisation

Avant d’utiliser une donnée externe, plusieurs questions doivent être posées :

  1. cette donnée est-elle obligatoire ?
  2. quel type de valeur attend-on ?
  3. quelles valeurs sont autorisées ?
  4. quelles limites faut-il appliquer ?
  5. cette donnée doit-elle être unique ?
  6. dans quel contexte sera-t-elle utilisée ?
  7. que doit faire l’application si elle est invalide ?

Cette dernière question est souvent sous-estimée.

Une application ne doit pas poursuivre son traitement normalement lorsqu’une donnée indispensable ne respecte pas les règles définies.

Séparer validation, stockage et affichage

Une bonne pratique consiste à distinguer trois étapes.

À l’entrée : valider

L’application vérifie que la donnée respecte le format et les règles attendues.

Lors du stockage : utiliser les protections adaptées

Pour une base SQL, cela signifie notamment utiliser des requêtes préparées.

À l’affichage : encoder

La donnée doit être encodée selon le contexte dans lequel elle apparaît.

Cette séparation évite une erreur classique : considérer qu’une donnée « nettoyée » une fois devient définitivement sûre.

Une même donnée peut être sans danger dans un contexte et problématique dans un autre.

Exemple complet de validation

Voici un exemple simple avec une adresse e-mail et un âge :

$email = filter_input(
    INPUT_POST,
    'email',
    FILTER_VALIDATE_EMAIL
);

$age = filter_input(
    INPUT_POST,
    'age',
    FILTER_VALIDATE_INT,
    [
        'options' => [
            'min_range' => 18,
            'max_range' => 120
        ]
    ]
);

if ($email === false || $email === null) {
    throw new InvalidArgumentException(
        'Adresse e-mail invalide'
    );
}

if ($age === false || $age === null) {
    throw new InvalidArgumentException(
        'Âge invalide'
    );
}

$stmt = $pdo->prepare(
    'INSERT INTO users (email, age)
     VALUES (:email, :age)'
);

$stmt->execute([
    'email' => $email,
    'age' => $age
]);

Cet exemple combine trois pratiques importantes :

  • valider les données ;
  • refuser les valeurs invalides ;
  • utiliser une requête préparée.

Et pour les données JSON ou les API ?

Les applications PHP modernes échangent souvent des données au format JSON.

Là encore, il faut vérifier chaque valeur.

Par exemple :

$data = json_decode(
    file_get_contents('php://input'),
    true,
    512,
    JSON_THROW_ON_ERROR
);

Ensuite, l’application doit contrôler chaque champ avant de l’utiliser.

if (
    !isset($data['email']) ||
    !filter_var($data['email'], FILTER_VALIDATE_EMAIL)
) {
    throw new InvalidArgumentException(
        'Adresse e-mail invalide'
    );
}

Le fait qu’une donnée provienne d’une API interne ne garantit pas qu’elle soit valide.

Contrôler aussi les types

PHP est un langage souple. Cette flexibilité peut toutefois provoquer des comportements inattendus.

Il est donc utile de définir des types lorsque cela est possible.

Par exemple :

function createUser(
    string $email,
    int $age
): void {
    // traitement
}

On peut également activer le typage strict :

declare(strict_types=1);

Le typage ne remplace pas la validation, mais il réduit certains risques liés aux conversions implicites.

Ne pas faire confiance uniquement au JavaScript

Une validation côté navigateur améliore l’expérience utilisateur.

Par exemple, un champ HTML peut imposer un format ou une valeur minimale.

Cependant, cette validation peut être contournée.

Un utilisateur peut modifier le code HTML, envoyer directement une requête HTTP ou désactiver JavaScript.

Il faut donc toujours valider les données côté serveur.

La validation côté client complète la validation PHP, mais ne la remplace jamais.

Journaliser les erreurs sans exposer d’informations sensibles

Lorsqu’une donnée invalide arrive, l’application peut enregistrer l’événement dans ses journaux.

Cependant, il faut éviter de conserver inutilement des données sensibles.

Les messages visibles par l’utilisateur doivent rester simples.

Par exemple :

Adresse e-mail invalide.

En revanche, les détails techniques peuvent rester dans les logs internes.

Cela évite d’exposer des informations sur le fonctionnement de l’application.

Bonnes pratiques à retenir

Pour sécuriser les données entrantes en PHP :

  • considérer toute donnée externe comme non fiable ;
  • valider selon le type attendu ;
  • ajouter des règles métier ;
  • privilégier les listes d’autorisation ;
  • rejeter les données invalides ;
  • utiliser des requêtes préparées ;
  • encoder les données à l’affichage ;
  • contrôler les fichiers téléversés ;
  • valider aussi les données provenant d’API ;
  • ne jamais dépendre uniquement de la validation côté navigateur.

La validation constitue une couche importante de sécurité, mais elle ne suffit pas à elle seule. Elle doit s’intégrer à une démarche plus large comprenant le contrôle d’accès, la gestion des sessions, la sécurité des dépendances et la configuration du serveur.

Pour une vision globale, cette fiche peut être reliée à la fiche Sécuriser une application PHP : risques et bonnes pratiques.

Damien BART

Damien BART pour INFORISQUE

Tags associés :

Partager :

Le Club, la communauté des professionnels de la prévention

Échangez avec vos pairs, partagez vos retours d’expérience, posez vos questions et développez votre réseau au sein de la plus grande communauté Santé & Sécurité au Travail de France.

Découvrir

Découvrez La newsletter du risque !

Chaque semaine, recevez gratuitement « La Lettre du Risque », la newsletter d’Inforisque, avec une sélection d’actualités, de guides, de webinaires et d’événements pour enrichir votre veille en santé et sécurité au travail.

S'abonner