08 nov. 2025·8 min de lecture

Validation côté serveur des entrées pour API qui rejette les absurdités

La validation côté serveur des entrées pour API empêche les mauvaises données d'entrer. Apprenez le parsing sûr, les schémas, les types stricts et les erreurs claires qui protègent réellement la production.

Validation côté serveur des entrées pour API qui rejette les absurdités

Pourquoi votre API continue d'accepter des absurdités

La plupart des API qui « acceptent n'importe quoi » ont été construites pour le happy path. Le code suppose que le client enverra les bons champs, sous la bonne forme et avec les bons types. Donc quand une requête arrive avec des champs manquants, des champs en trop, des chaînes là où il faut un nombre, ou un objet complètement différent, le serveur l'ajuste discrètement, continue et renvoie quand même un 200.

C'est particulièrement courant avec des endpoints générés par IA. Ils désérialisent souvent le JSON directement en objets, évitent les contrôles stricts et se reposent sur « ça marchait dans la démo ». Le résultat : une API qui a l'air correcte lors de tests rapides, mais qui se comporte de façon imprévisible quand de vrais utilisateurs, des intégrations réelles ou des attaquants arrivent.

Les contrôles côté client ne vous protègent pas. Les navigateurs peuvent être contournés, les applis mobiles modifiées et des clients tiers peuvent envoyer n'importe quel payload. Même des clients bien intentionnés peuvent se désynchroniser après une release et commencer à envoyer des champs que votre serveur n'attendait pas.

En production, une validation faible se transforme en problèmes coûteux :

  • Plantages et timeouts quand le code tombe sur un type inattendu ou un null
  • Mauvaises entrées qui semblent valides mais cassent les rapports et les workflows plus tard
  • Bugs de sécurité quand des entrées non fiables atteignent des requêtes, des chemins de fichiers ou la logique d'auth
  • Douleur pour le debug parce que la même entrée erronée échoue différemment selon les endroits

L'objectif de la validation côté serveur des entrées pour API est simple : rejeter les mauvaises entrées tôt, de façon cohérente et sûre. Cela signifie un point de contrôle clair à la frontière de votre API qui vérifie la forme, les types et les limites avant que votre logique métier ne s'exécute.

Les équipes viennent souvent chez FixMyMess avec des endpoints qui « fonctionnent » mais acceptent des absurdités comme des emails vides, des quantités négatives ou des objets là où il devrait y avoir un ID. La solution est rarement compliquée : ajouter une validation stricte en amont pour que les absurdités n'entrent pas dans le système.

Validation, assainissement et parsing : la différence simple

Si vous voulez qu'une API cesse d'accepter des absurdités, vous devez séparer trois tâches souvent mélangées : validation, assainissement (sanitization) et parsing.

La validation répond à : « Cette entrée est-elle autorisée ? » Elle vérifie la forme et les règles : champs obligatoires, types, plages, longueurs, formats et valeurs autorisées. Une bonne validation côté serveur rejette les mauvaises requêtes tôt, avant qu'elles n'atteignent la base, la logique d'auth ou des appels tiers.

L'assainissement répond à : « Si cette entrée est autorisée, comment la rendre sûre à stocker ou afficher ? » Exemples : échapper du texte avant rendu HTML, supprimer caractères de contrôle, ou retirer du balisage inattendu. L'assainissement ne remplace pas la validation. Une chaîne nettoyée peut toujours être du mauvais type, trop longue ou manquer de champs clés.

Le parsing (et la normalisation) répond à : « Transformer des données inconnues en une forme connue. » Vous décodez le JSON, convertissez des chaînes en nombres, appliquez des valeurs par défaut et normalisez des formats (par ex. mettre un email en minuscules). Le schéma sûr est : parser, puis utiliser. Le pattern risqué est : utiliser, puis espérer.

Un exemple simple : un endpoint d'inscription reçoit age: \"25\" (string), email: \"[email protected] \" et role: \"admin\". Un flux sûr est :

  • Parser et normaliser : trim de l'email, le passer en minuscules, convertir age en nombre
  • Valider : age doit être 13–120, role doit être l'une des valeurs autorisées
  • Ce n'est qu'après : créer l'utilisateur

Ce « contrat d'entrée strict » (un schéma avec types et limites clairs) est le minimum. Il n'empêchera pas toutes les attaques à lui seul, mais bloque les déchets accidentels, réduit les bugs en bord de cas et facilite la bonne mise en place des contrôles de sécurité.

Façons courantes dont une validation faible casse des systèmes réels

La validation faible échoue rarement de façon propre. Elle « fonctionne » généralement en test, puis casse sous trafic réel parce que des utilisateurs réels, des bots et des clients bugués envoient des entrées étranges.

Une panne courante est l'over-posting. Un client envoie des champs supplémentaires que vous n'aviez pas prévus, et votre code les utilise (ou les stocke) parce qu'il étale tout le body dans un write en base. Cela peut inverser des flags comme isAdmin, changer des champs de tarification ou écraser des réglages internes sans que personne ne s'en aperçoive.

Autre problème : la confusion de types. Si vous attendez un nombre mais acceptez une chaîne, vous avez des surprises : \"10\" devient 10 à un endroit, reste une chaîne ailleurs, et soudain tri, comparaisons et calculs sont faux. Pire encore, \"\", \"NaN\" ou \"001\" peuvent passer et créer des cas difficiles à déboguer.

Les limites sont l'endroit où le code happy path s'effondre. Sans checks de taille, une seule requête peut envoyer une chaîne de 5 Mo, un tableau de 50 000 éléments ou du JSON profondément imbriqué qui fait exploser CPU et mémoire. L'API ne plante pas à chaque fois, mais ralentit, timeoute et provoque des défaillances en cascade.

Les problèmes de sécurité surviennent quand vous faites confiance aux entrées trop tôt. Si des données non validées sont utilisées dans des requêtes SQL, des filtres, des chemins de fichiers ou du HTML, vous ouvrez la porte aux injections et fuites de données.

Voici les patterns qui reviennent quand la validation côté serveur des entrées pour API manque :

  • Écrire des champs « inconnus » en base car le payload n'est pas en allowlist
  • Coercir implicitement les types puis prendre des décisions sur la mauvaise valeur
  • Autoriser des chaînes/tableaux sans limites, provoquant ralentissements et outages
  • Passer l'entrée brute dans des requêtes ou templates avant qu'elle ne soit vérifiée et parsée

FixMyMess retrouve souvent ces problèmes dans des endpoints générés par IA : ils paraissent propres, mais acceptent presque n'importe quel JSON et espèrent que le code en aval gère. Les systèmes réels ont besoin du contraire : rejeter les absurdités tôt, clairement et de façon cohérente.

Choisir une approche de schéma adaptée à votre stack

L'objectif de la validation côté serveur des entrées pour API est simple : chaque requête est vérifiée contre un contrat clair avant d'atteindre la logique métier. Le moyen le plus simple est d'utiliser un schéma qui peut à la fois valider et parser en toute sécurité les entrées dans les types attendus par votre code.

En JavaScript/TypeScript, des bibliothèques comme Zod ou Joi sont populaires car elles peuvent coercer les types avec contrôle (si vous l'autorisez) et donner des erreurs lisibles. En Python, Pydantic est courant parce qu'il transforme les données entrantes en modèles stricts avec defaults. Si vous utilisez déjà une approche OpenAPI-first ou JSON Schema, rester avec JSON Schema peut garder docs et validation alignés. Beaucoup de frameworks ont aussi des validateurs intégrés, suffisants pour de petites API.

Où exécuter la validation importe. Deux patterns courants :

  • Middleware qui valide body, query et params avant que le handler ne s'exécute
  • Validation à l'intérieur de chaque route, proche du code qui utilise les données

Le middleware est plus simple à garder cohérent. La validation par handler peut être plus claire si chaque route a des règles spéciales. Dans tous les cas, essayez de faire du schéma la source unique de vérité pour body, query et path params, afin de ne pas finir par valider le body et oublier les params.

Un exemple concret : un endpoint attend limit (number) dans la query et email (string) dans le body. Sans schéma, du code généré par IA accepte souvent limit=\"ten\" ou email=[] et échoue plus tard de façon confuse. Avec un schéma, ces cas sont rejetés immédiatement avec une erreur claire.

Enfin, planifiez le changement. Quand votre contrat d'API évolue, versionnez vos schémas comme vous versionnez endpoints ou clients. Conservez les vieux schémas pour les anciens clients et introduisez les nouveaux avec une bascule claire. C'est une correction fréquente que nous appliquons chez FixMyMess quand des équipes héritent de prototypes IA rapides et doivent les sécuriser sans tout réécrire.

Étape par étape : ajouter la validation côté serveur à un endpoint

Choisissez un endpoint qui pose problème, comme POST /users ou POST /checkout. L'objectif : une validation côté serveur qui rejette les absurdités avant que votre logique métier ne s'exécute.

1) Définir un petit schéma (seulement ce dont vous avez besoin)

Commencez par les champs requis seulement. Si l'endpoint crée un utilisateur, vous avez peut-être vraiment besoin de email et password, pas de 12 champs optionnels « au cas où ». Gardez le schéma strict et explicite.

Validez chaque source d'entrée séparément : path params, query params et body. Traitez-les comme des bacs différents avec des risques différents.

// Pseudocode schema
const bodySchema = {
  email: { type: "email", required: true, maxLen: 254 },
  password: { type: "string", required: true, minLen: 12, maxLen: 72 }
};
const querySchema = { invite: { type: "string", required: false, maxLen: 64 } };
const pathSchema = { orgId: { type: "uuid", required: true } };

2) Valider + parser avant toute autre chose

Faites de la validation les premières lignes du handler. Ne « corrigez » pas des types au hasard plus tard.

  • Parser body, query et path séparément en valeurs typées
  • Rejeter les champs inconnus (mode strict) pour que les clés en trop ne passent pas
  • Ajouter des bornes : min/max, limites de longueur et contrôles de format simples

Un exemple concret : si quelqu'un envoie { \"email\": [], \"password\": true, \"role\": \"admin\" }, le parsing strict doit le rejeter. Il ne doit pas coercer les types et il ne doit surtout pas accepter role si votre schéma ne le permet pas.

3) Ajouter des tests pour les mauvaises entrées

Un bon test invalide vaut dix tests happy-path. Essayez champs manquants, mauvais types, champs en trop, chaînes énormes, nombres négatifs et encodages bizarres. C'est souvent là que les endpoints générés par IA échouent, et c'est le type de correctif que les équipes nous demandent chez FixMyMess quand un prototype arrive en production.

Patterns de parsing sûrs qui évitent les bugs

Détecter rapidement les lacunes de validation
Envoyez votre code et nous identifierons où votre API accepte des entrées incorrectes et pourquoi.

La plupart des bugs ne sont pas causés par une « validation manquante », mais par ce qui se passe après la validation. L'habitude la plus sûre est de parser l'entrée une fois, obtenir un succès ou un échec clair, puis ne travailler qu'avec le résultat parsé.

Parser d'abord, puis oublier la requête brute

Une bonne librairie de schéma vous donne une API « safe parse » : elle retourne soit une valeur parsée fiable, soit une erreur que vous renvoyez au client. C'est le cœur de la validation côté serveur.

// Example shape, works similarly in many schema libraries
const result = UserCreateSchema.safeParse(req.body);
if (!result.success) {
  return res.status(400).json({ error: "Invalid input", fields: result.error.fields });
}

const input = result.data; // only use this from now on
// Never touch req.body again in this handler
createUser({ email: input.email, age: input.age });

Ce simple changement évite un bug fréquent des endpoints générés par IA : le code valide, puis lit quand même req.body plus tard et accepte accidentellement des champs en plus, des types wrong ou des cas limites.

Normaliser uniquement quand c'est voulu

La normalisation peut aider, mais elle doit être volontaire, pas accidentelle.

  • Trimmez les chaînes seulement pour les champs où les espaces n'ont pas de sens (emails, noms d'utilisateur).
  • Choisissez une règle de casse quand les comparaisons importent (par ex. emails en minuscules).
  • Convertissez les dates en objets Date seulement si votre schéma garantit le format.
  • Gardez les IDs exacts ; ne les trimmez pas ou ne les modifiez pas sauf si votre système l'impose.

Évitez la « coercition magique »

Faites attention à la coercition comme transformer "123" en 123. Cela cache les mauvaises entrées et rend plus difficile la détection des bugs clients ou des abus. Coercez seulement quand vous en avez vraiment besoin (par ex. query params qui sont toujours des chaînes), et quand vous le faites, imposez des limites (min, max, entiers seulement) pour ne pas accepter des absurdités comme "999999999999".

Si vous avez hérité d'une API générée par IA qui « marche » mais accepte des déchets, ce pattern parser-puis-utiliser-uniquement-les-données-parsées est un des correctifs les plus rapides à appliquer en toute sécurité.

Types stricts, valeurs par défaut et limites qui comptent

La validation stricte ne se résume pas à « le champ est présent ». Il s'agit de faire accepter à votre API seulement les formes et valeurs que vous acceptez réellement. Une bonne validation côté serveur commence à la frontière : body, query et headers.

Les valeurs par défaut appartiennent à la frontière

Mettez les valeurs par défaut pendant le parsing, pas au cœur de votre logique métier. Ainsi, tout le code en aval peut supposer une forme complète et connue.

Exemple : si page manque, définissez-le par défaut à 1 pendant le parsing. Ne laissez pas des parties aléatoires du code décider plus tard, sinon vous aurez des comportements différents selon les endpoints.

Décidez aussi ce que signifie « manquant ». Un champ manquant n'est pas la même chose qu'un champ à null.

  • Optional : le client peut l'omettre (ex : middleName non fourni).
  • Nullable : le client peut envoyer null (ex : deletedAt: null quand pas supprimé).

Traitez-les différemment dans votre schéma. Sinon vous aurez des bugs bizarres comme null passant la validation mais cassant du code qui attend une chaîne.

Les enums et limites arrêtent des familles entières de bugs

Si vous connaissez les valeurs autorisées, dites-le. Les enums empêchent des chaînes « presque correctes » (comme adminn) de se faufiler et créer des états cachés.

Un exemple réaliste : un endpoint généré par IA prend status et sort en query. Sans types stricts, status=donee peut être traité comme truthy et renvoyer les mauvais enregistrements, et sort=DROP TABLE peut être concaténé dans une requête.

Ajoutez des limites qui protègent votre base et votre porte-monnaie :

  • Limitez limit (par ex. max 100)
  • Verrouillez page à >= 1
  • Whitelist des champs sortBy (ex : createdAt, name)
  • Restreignez order à asc ou desc
  • Définissez des longueurs max pour les chaînes (noms, emails, termes de recherche)

Ce sont des corrections courantes que nous appliquons pour réparer des prototypes IA chez FixMyMess, parce que le code happy path accepte souvent tout et ne casse qu'en production quand les utilisateurs envoient du chaos réel.

Réponses d'erreur utiles sans trop en dire

Audit de code gratuit pour les APIs
Nous passons en revue vos routes les plus risquées et livrons un plan clair avant tout engagement.

Quand l'entrée est mauvaise, votre API doit être claire et ennuyeuse : renvoyez un 400, expliquez quoi corriger et gardez la forme de la réponse identique à chaque fois. Une forme stable aide les clients à gérer les erreurs sans cas spéciaux et empêche l'équipe de fuir des détails sensibles.

Un pattern simple est une enveloppe d'erreur plus des détails par champ :

{
  "error": {
    "code": "INVALID_INPUT",
    "message": "Some fields are invalid.",
    "fields": [
      { "path": "email", "message": "Must be a valid email address." },
      { "path": "age", "message": "Must be an integer between 13 and 120." }
    ]
  }
}

Gardez les messages concentrés sur ce que l'appelant peut changer. Évitez d'échoer l'entrée brute (surtout des chaînes qui pourraient contenir du HTML ou du SQL) et n'incluez jamais de traces de pile, d'erreurs SQL, d'IDs internes ou de noms de librairies. Au lieu de "ZodError: Expected number, received string", dites "age must be a number".

Les librairies de schéma donnent souvent des erreurs détaillées. Mappez-les vers votre format d'API et gardez les chemins de champs prévisibles (les chemins en dot fonctionnent bien). S'il y a beaucoup de problèmes, limitez le nombre retourné (par ex. les 10 premiers) pour que la réponse reste petite.

Pour votre debugging, loggez les échecs de validation de manière sûre. Enregistrez :

  • l'ID de requête et l'endpoint
  • les chemins de champs qui ont échoué (pas les valeurs brutes)
  • les métadonnées de l'appelant déjà de confiance (user ID, tenant)
  • un instantané redacté du payload

C'est une correction typique dans du code d'API généré par IA : l'UI obtient un retour utilisateur sympa, tandis que vos logs expliquent pourquoi les requêtes ont été rejetées, sans exposer de secrets.

Pièges courants lors de l'ajout de validation

La plus grosse erreur est de valider trop tard. Si vous lisez des champs, construisez une requête SQL ou écrivez en stockage avant de vérifier les valeurs, le mal est déjà fait. La validation doit se faire à la frontière : parser, valider et seulement ensuite toucher le reste du code.

Un autre piège est de faire confiance à des types qui n'existent qu'au moment de la compilation. Le code généré par IA a souvent l'air « typé » mais accepte encore n'importe quoi au runtime. Une annotation UserId: string n'empêche pas \"\", \" \" ou \"DROP TABLE\" d'arriver. La validation côté serveur doit s'exécuter à chaque requête, pas seulement dans votre éditeur.

« Autoriser les champs inconnus pour la flexibilité » a aussi tendance à se retourner contre vous. Les champs supplémentaires deviennent un refuge pour bugs, clients confus et problèmes de sécurité (comme un role: \"admin\" qui se fond dans votre objet user). Si vous avez besoin de compatibilité ascendante, acceptez seulement les champs connus et versionnez l'API quand la forme change.

Les frameworks peuvent saboter votre intention en coercant silencieusement les types. Si \"false\" devient true ou \"123abc\" devient 123, vous déployez des bugs logiques difficiles à reproduire. Préférez un parsing strict qui échoue vite avec une erreur claire.

Enfin, beaucoup d'équipes oublient les limites, donc la validation passe mais votre serveur souffre quand même. Quelques gardes de base préviennent les abus et accidents :

  • Limiter la taille du body et rejeter les payloads trop gros tôt
  • Mettre des longueurs max pour les chaînes (noms, emails, IDs)
  • Limiter les tableaux (items par requête) et la profondeur d'objet imbriqué
  • Exiger des formats explicites (UUIDs, dates ISO) et rejeter le « presque correct »
  • Garder les valeurs par défaut intentionnelles ; ne pas remplir automatiquement des champs de façon à masquer des bugs client

Un exemple pratique : un endpoint généré par IA pour « mettre à jour un profil » peut accepter une bio de 5 Mo, un objet imbriqué là où une chaîne était attendue et des clés inconnues qui écrasent des données stockées. C'est le genre de nettoyage que FixMyMess trouve souvent lors d'audits : la validation existe mais est permissive exactement aux mauvais endroits.

Checklist rapide pour des entrées d'API plus sûres

Si vous voulez que la validation côté serveur des entrées pour API bloque réellement les mauvaises données, gardez une checklist courte et appliquez-la à chaque endpoint que vous touchez. La plupart des bugs « ça marche sur ma machine » viennent de l'omission d'une de ces bases.

Commencez par valider chaque surface d'entrée, pas seulement les bodies JSON. Les query strings et les route params ne sont pas dignes de confiance non plus et finissent souvent par contrôler filtres, IDs et pagination.

Utilisez cette checklist comme barre minimale :

  • Validez body, query et params avant toute logique métier ou appel DB.
  • Préférez les allowlists : rejetez les champs inconnus à moins d'avoir besoin d'un payload flexible.
  • Mettez des limites partout : longueur max de chaîne, plages numériques, tailles d'array et caps de pagination.
  • Parsez en un objet typé et validé et n'utilisez que ce résultat parsé. Si le parsing échoue, échouez fermé.
  • Ajoutez quelques cas de test méchants par endpoint : mauvais types, champs manquants, champs en trop et inputs surdimensionnés.

Un exemple rapide : votre endpoint attend { \"email\": \"...\", \"age\": 18 }. Une requête réelle peut envoyer age: \"18\", age: -1 ou ajouter \"isAdmin\": true. Si vous coercez silencieusement les types ou acceptez des clés supplémentaires, vous apprenez à votre système à accepter des absurdités et, pire, des changements de privilèges.

Si vous avez hérité d'un code d'API généré par IA, cette checklist est souvent le point de départ des corrections. Les équipes travaillant avec FixMyMess trouvent généralement le même pattern : le happy path fonctionne, mais le serveur fait confiance à tout ce qui arrive. Une validation stricte + parsing strict est l'une des manières les plus rapides de transformer un prototype fragile en quelque chose d'exploitable en production.

Exemple : réparer un endpoint IA qui accepte des déchets

Reconstruire proprement depuis zéro
Quand les correctifs sont trop compliqués, nous pouvons reconstruire le backend en ~24 heures.

Une histoire fréquente : un outil IA génère un endpoint /signup qui « marche localement » parce que vous n'avez essayé que le happy path. En production, il commence à accepter des absurdités et les dégâts sont silencieux.

Voici un pattern de défaillance réel. L'endpoint attend :

  • email (string)
  • password (string)
  • plan ("starter" | "pro")

Mais l'API accepte volontiers ce payload :

{ "email": ["[email protected]"], "password": true, "plan": "pro ", "coupon": {"code":"FREE"} }

Ensuite, c'est le bazar : l'email finit par être "[email protected]" (tableau stringifié), le password booléen est coercé, le plan a un espace final donc la tarification retombe sur un défaut, et l'objet coupon inattendu peut être stocké comme "[object Object]" ou faire planter un job plus tard. Vous obtenez de l'auth cassée, une facturation erronée et des lignes corrompues difficiles à nettoyer.

La solution n'est pas de « tout assainir ». C'est la validation côté serveur avec parsing strict.

Raccourci de réparation

Définissez un schéma qui rejette les champs inconnus, trim/normalise les chaînes sûres et impose les types. Puis parsez avant toute opération.

  • Validez champs requis et types (pas de coercition par défaut)
  • Normalisez les valeurs sûres (trim plan, lowercase email)
  • Rejetez les extras (échec ou rejet sur coupon)
  • Appliquez des limites (longueurs max, règles de mot de passe)

Quand le parsing échoue, renvoyez un 400 clair :

"Invalid request: plan must be one of starter, pro; email must be a string; unexpected field coupon"

Résultat : moins d'incidents en production, données plus propres et debug plus rapide parce que les mauvaises entrées s'arrêtent à la porte. Si vous avez hérité d'un endpoint IA comme celui-ci, FixMyMess trouve typiquement ces points faibles en audit rapide et les corrige sans réécrire toute l'application.

Étapes suivantes : améliorer la sécurité sans tout réécrire

Si vous voulez que la validation côté serveur des entrées pour API rapporte vite, ne commencez pas par « valider tout ». Commencez là où les mauvaises entrées causent de vrais dégâts : endpoints qui écrivent des données ou déclenchent des travaux coûteux.

Choisissez trois endpoints d'abord. Un bon trio : login/signup (auth), tout ce qui touche à l'argent (paiements) et toute action admin. Ajoutez la validation à la frontière (body, query, headers) avant d'accéder à la base, aux fichiers, aux queues ou aux API tierces. Ce seul geste empêche la plupart des bugs d'"acceptation de n'importe quoi".

Un plan de déploiement simple qui évite la réécriture :

  • Ajoutez un schéma par endpoint et rejetez par défaut les champs inconnus.
  • Fixez des limites strictes (longueur de chaîne, taille d'array, plages numériques, taille max de page).
  • Normalisez et parsez une seule fois (trim, parsing de date, coercition numérique uniquement si voulu).
  • Ajoutez du monitoring sur les échecs de validation pour voir ce que les clients envoient.

Les clients legacy sont la partie délicate. Si vous ne pouvez pas les casser du jour au lendemain, autorisez une tolérance temporaire de façon contrôlée : acceptez l'ancien format, convertissez-le en interne vers la nouvelle forme stricte et loggez chaque fois que vous avez dû « sauver » l'entrée. Mettez une date de dépréciation explicite pour que cela n'ait pas vocation à durer.

Si votre API a été générée par un outil IA et que le code semble déjà emmêlé (handlers copiés-collés, types incohérents, bugs d'auth cachés), un audit ciblé est souvent plus rapide que des correctifs patch par patch. FixMyMess peut faire un audit gratuit et réparer les chemins d'API les plus risqués (validation, auth, secrets et sécurité) pour que vous atteigniez un état « sûr par défaut » sans re-construction complète.