Un formulaire PHP ne devient pas sûr parce que ses champs sont contrôlés dans le navigateur. Les validations JavaScript améliorent l’expérience utilisateur, mais elles peuvent être contournées avant l’envoi. La protection réelle doit donc être appliquée côté serveur, au moment où PHP reçoit et traite les données.
Une base solide repose sur trois mécanismes complémentaires : valider les valeurs reçues, échapper les données lorsqu’elles sont réaffichées et vérifier un jeton CSRF pour s’assurer que la requête vient bien de votre formulaire. Cette méthode convient à un formulaire de contact, d’inscription ou de modification de profil, avec des adaptations selon les données manipulées.
Commencer par définir ce que le formulaire est autorisé à recevoir

La sécurité commence avant même l’écriture du code. Pour chaque champ, précisez le type attendu, le caractère obligatoire ou facultatif, la longueur acceptable et les éventuelles valeurs autorisées. Un champ « téléphone » n’obéit pas aux mêmes règles qu’un champ « message », et un identifiant interne ne devrait jamais être traité comme un texte libre.
Cette approche correspond à une validation par liste blanche : vous décrivez ce qui est acceptable au lieu d’essayer d’interdire toutes les variantes dangereuses. Elle réduit les ambiguïtés et rend les règles plus faciles à maintenir.
| Champ | Contrôles à prévoir | Décision en cas d’erreur |
|---|---|---|
| Adresse e-mail | Présence, longueur raisonnable et format valide | Refuser l’envoi et demander une correction |
| Nom | Présence, longueur minimale et maximale, caractères acceptés | Nettoyer uniquement les espaces superflus puis valider |
| Message | Présence, taille maximale et éventuelles règles métier | Conserver le texte comme donnée, sans l’interpréter comme HTML |
| Identifiant ou choix | Type attendu et appartenance à une liste disponible | Refuser toute valeur inconnue |
Ne confondez pas nettoyage et validation. Supprimer automatiquement certains caractères peut modifier le sens d’une information ou masquer une tentative anormale. Dans de nombreux cas, il vaut mieux refuser une valeur qui ne respecte pas les règles prévues et expliquer clairement le problème à l’utilisateur.
Valider côté serveur sans faire confiance aux champs cachés
À la réception d’une requête POST, vérifiez d’abord que la méthode HTTP correspond à l’action attendue. Récupérez ensuite les valeurs de façon contrôlée, en distinguant un champ absent d’un champ vide. Une valeur fournie par le navigateur, y compris dans un champ caché, doit être considérée comme modifiable par l’utilisateur.
Pour les textes, contrôlez la longueur avant tout traitement coûteux. Pour un nombre, vérifiez qu’il s’agit réellement d’un nombre et qu’il se situe dans l’intervalle autorisé. Pour une option, comparez la valeur reçue avec les choix connus par l’application. Pour un fichier, le nom transmis par le navigateur ne suffit pas : il faut contrôler la taille, le type détecté et le chemin de stockage.
- Initialiser une collection d’erreurs avant de traiter les champs.
- Lire chaque valeur avec une valeur par défaut adaptée à son type.
- Appliquer les règles métier prévues pour le champ.
- Ajouter un message compréhensible si une règle échoue.
- Arrêter l’enregistrement ou l’action tant qu’une erreur subsiste.
Les messages affichés à l’utilisateur peuvent rester simples : « indiquez une adresse e-mail valide » ou « votre message dépasse la longueur autorisée ». Les détails techniques, comme une trace interne ou une requête reçue, ne doivent pas être révélés dans la page.
Pourquoi la validation HTML ne suffit pas
Les attributs HTML comme le caractère obligatoire ou le type e-mail sont utiles pour guider l’utilisateur, mais ils ne constituent pas une barrière de sécurité. Un client HTTP peut envoyer directement une requête sans respecter les contraintes affichées dans le navigateur. La validation serveur doit donc toujours reprendre les règles essentielles.
Échapper les données au moment où elles ressortent
Une donnée peut être acceptable dans la base de données et devenir dangereuse lorsqu’elle est insérée dans une page HTML. C’est pourquoi l’échappement ne doit pas être effectué systématiquement à l’entrée. Il dépend du contexte de sortie : texte HTML, attribut, URL, JavaScript ou requête SQL ne se traitent pas de la même manière.
Lorsqu’un nom ou un message est réaffiché dans une page HTML, encodez les caractères spéciaux au moment de construire la réponse. Ainsi, une chaîne contenant des balises ne sera pas interprétée comme du code par le navigateur. Cette règle est particulièrement importante lorsque le formulaire réaffiche les valeurs saisies après une erreur.
Évitez également de concaténer directement des valeurs utilisateur dans une requête SQL. Pour la base de données, utilisez des requêtes préparées et transmettez les valeurs séparément. L’échappement HTML protège l’affichage ; il ne remplace pas la protection adaptée à SQL, aux URL ou aux scripts.
Règle pratique : validez la donnée selon son métier, puis échappez-la selon le contexte dans lequel elle sera utilisée.
Ajouter un jeton CSRF aux formulaires qui modifient un état

Une attaque CSRF, ou falsification de requête intersite, exploite une session déjà ouverte. Un utilisateur connecté peut être amené à visiter une page externe qui tente de déclencher une action sur votre site : modification d’un profil, changement d’adresse e-mail ou suppression d’un élément. Le navigateur peut joindre automatiquement certains cookies de session, mais le site attaqué ne connaît pas nécessairement le jeton secret généré par votre application.
Le principe consiste à générer un jeton aléatoire côté serveur, à le conserver dans la session et à l’insérer dans le formulaire. Lors de la réception, PHP compare la valeur envoyée avec celle attendue. Un jeton manquant, expiré ou différent doit entraîner le refus de l’action.
- Démarrer la session avant tout envoi de contenu.
- Générer un jeton imprévisible avec une fonction adaptée à la génération de valeurs aléatoires.
- Conserver ce jeton dans la session serveur.
- Placer sa valeur dans un champ caché du formulaire.
- Comparer la valeur reçue avec celle de la session en utilisant une comparaison résistante aux attaques temporelles.
- Supprimer ou renouveler le jeton après une action sensible si le scénario le justifie.
Le champ caché n’est pas secret : un utilisateur peut le voir dans le code de la page. Sa valeur doit toutefois être difficile à deviner et liée à la session concernée. Le jeton CSRF ne remplace pas l’authentification, le contrôle des autorisations ou la protection de la session ; il complète ces mécanismes.
Traiter les erreurs sans perdre les données ni créer une nouvelle faille
Un formulaire bien conçu ne renvoie pas simplement une erreur générique après chaque échec. Il conserve les valeurs non sensibles, affiche les corrections attendues et place le curseur sur le premier champ problématique lorsque l’interface le permet. Les mots de passe, jetons et autres secrets ne doivent pas être réaffichés.
Avant de réutiliser une valeur dans le formulaire, échappez-la pour le contexte HTML. Si le message d’erreur reprend une partie de la saisie, appliquez la même prudence. Ne placez pas directement une donnée utilisateur dans un script intégré, un attribut d’événement ou une URL sans traitement spécifique : ce sont des contextes différents du texte courant.
Après un traitement réussi, utilisez généralement le schéma Post/Redirect/Get. Le serveur traite le POST, enregistre l’action puis redirige vers une page de résultat. L’utilisateur peut ainsi actualiser la page sans renvoyer accidentellement le formulaire, et le message de succès peut être transmis temporairement par la session.
Ne pas oublier les limites du formulaire dans l’application
La validation du formulaire ne suffit pas si l’étape suivante ne vérifie pas les droits de l’utilisateur. Un formulaire peut contenir un identifiant appartenant à un autre compte : il faut contrôler côté serveur que l’utilisateur connecté a bien le droit de consulter ou de modifier la ressource visée.
Ajoutez aussi une limite de fréquence lorsque l’action peut être automatisée ou coûteuse, par exemple l’envoi de messages, la demande de réinitialisation ou la création de comptes. Un captcha n’est pas une réponse universelle : il peut compléter une stratégie anti-abus, mais ne corrige ni une validation absente ni une autorisation mal contrôlée.
Enfin, évitez d’afficher des erreurs détaillées en production. Enregistrez les informations utiles dans un système de journalisation protégé, sans y stocker de mots de passe, de jetons CSRF ou de données personnelles en clair lorsque cela n’est pas nécessaire.
Checklist avant la mise en ligne

- Le serveur refuse-t-il les champs absents, trop longs ou d’un type inattendu ?
- Les règles sont-elles appliquées même lorsque JavaScript est désactivé ?
- Les valeurs réaffichées sont-elles échappées au bon endroit ?
- Les requêtes SQL utilisent-elles des paramètres séparés plutôt qu’une concaténation ?
- Un jeton CSRF est-il exigé pour chaque action qui modifie des données ou une session ?
- Le serveur contrôle-t-il les autorisations sur la ressource ciblée ?
- Les secrets sont-ils exclus des messages, des champs réaffichés et des journaux ?
- Le formulaire résiste-t-il aux doubles envois et aux soumissions répétées ?
- Les limites de taille et de fréquence sont-elles adaptées à l’usage réel ?
Un formulaire PHP sûr n’est donc pas une simple fonction de nettoyage placée au début du script. C’est une chaîne de contrôles : déterminer la donnée attendue, la valider côté serveur, vérifier le contexte de la requête, contrôler les droits, puis protéger chaque sortie. En appliquant cette méthode dès le premier formulaire, vous évitez de devoir corriger plus tard une logique dispersée entre le navigateur, le serveur et la base de données.