Parsing sûr de CSV et JSON : protégez les uploads contre les entrées malveillantes
Le parsing sûr de CSV et JSON évite l'injection de formules, les lignes malformées et les pics mémoire quand des utilisateurs uploadent des fichiers.

Pourquoi les CSV et JSON fournis par les utilisateurs sont risqués
Les uploads d'utilisateurs ne sont pas fiables. Ça paraît évident, mais ça change tout. Les données internes suivent généralement vos règles parce que votre propre code les a produites. Les fichiers uploadés viennent d'outils inconnus, de paramètres inconnus, et parfois d'intentions inconnues.
CSV et JSON ont aussi l'air inoffensifs parce que ce sont "juste du texte." Mais le texte peut quand même déclencher des bugs, produire de mauvais enregistrements ou glisser du contenu que d'autres parties de votre système exécuteront. Un petit fichier peut faire des dégâts s'il touche le bon point faible, comme un cas limite du parseur ou une formule de tableur.
Dans la pratique ça se traduit par des douleurs réelles : des imports qui plantent et provoquent des indisponibilités, des lignes corrompues qui polluent la base de données, des exports qui déclenchent une injection de formule CSV quand on les ouvre dans un tableur, et des uploads volumineux qui ralentissent tout quand vous chargez tout en mémoire.
"Parsing sûr" ne veut pas dire "mon code peut lire le fichier." Ça veut dire que vous contrôlez ce qui se passe quand le fichier est bizarre, hostile ou simplement plus grand que prévu. En pratique, il s'agit de définir des frontières (taille, temps, nombre de lignes), de lire de façon prévisible (streaming au lieu de tout charger d'un coup) et de valider chaque enregistrement avant qu'il n'atteigne la base.
Un scénario réaliste est un import de contacts uploadé par un client. Le fichier peut contenir une seule cellule avec une chaîne de 5 Mo, un champ JSON avec un nesting inattendu, ou un nom qui commence par =HYPERLINK(...). Si vous traitez l'upload comme une entrée de confiance, vous risquez une indisponibilité, un nettoyage de données désordonné et un incident de sécurité.
Les principaux modes de défaillance à anticiper
Les uploads utilisateurs échouent de façons prévisibles. Nommez-les dès le départ et le parsing sûr devient ennuyeux plutôt que risqué.
CSV malformé est le problème classique. Les fichiers réels ont souvent des quotes cassées, des virgules en trop, des nombres de colonnes inégaux ou des encodages étranges qui transforment des caractères en charabia. Certains parseurs tentent de deviner l'intention de l'utilisateur. Cela peut décaler silencieusement des colonnes et corrompre vos données, ou faire planter un job d'import quand une seule ligne est très malformée.
Injection de formule CSV est plus sournoise. Les tableurs traitent les cellules commençant par =, +, - ou @ comme des formules. Si vous exportez ensuite les données et que quelqu'un ouvre le fichier dans Excel ou Google Sheets, un attaquant peut planter une cellule qui exécute une formule. Le danger est souvent en aval : le flux de travail humain.
Pièges JSON se manifestent différemment. Des objets profondément imbriqués peuvent atteindre les limites de récursion ou faire exploser le CPU. De grands tableaux peuvent transformer un "petit upload" en minutes de traitement. Les clés dupliquées sont un autre piège : certains parseurs gardent la première valeur, d'autres la dernière. Les attaquants peuvent exploiter cette ambiguïté pour contourner la validation.
Explosions de mémoire sont le mode d'échec le plus simple. Lire l'intégralité du fichier en mémoire (ou construire un objet en mémoire complet avant de valider) transforme les uploads volumineux en timeouts et plantages. Un upload de 50 Mo peut devenir beaucoup plus grand une fois parsé.
Un exemple pratique : un upload "contacts.csv" semble correct, mais une ligne a une quote non fermée. Votre parseur décale les colonnes, et maintenant le champ email contient des morceaux d'autres colonnes. Ou un "contacts.json" inclut un contact avec un tableau de notes massif et la mémoire monte en flèche.
Commencez par des frontières strictes avant de parser
La plupart des bugs d'upload arrivent avant même que votre parseur ait une chance d'aider. Si vous acceptez n'importe quoi qui ressemble à un tableur ou à "du JSON", vous invitez des cas limites que vous n'avez jamais testés.
Décidez ce que vous supportez réellement. Si une fonctionnalité n'a besoin que d'une table simple, acceptez CSV et rejetez JSON entièrement (et vice versa). Supporter moins de formats élimine beaucoup de coins étranges.
Ensuite, appliquez des limites strictes qui correspondent à votre usage réel. La taille du fichier ne suffit pas. Un petit fichier peut contenir un million de petits champs, et un fichier normal peut s'étendre en mémoire si vous le chargez entièrement.
Frontières qu'il vaut la peine d'imposer
Pour des imports typiques comme des contacts, un petit ensemble de garde‑fous fait la plupart du travail :
- Octets max (selon les limites produit)
- Lignes max et colonnes max
- Longueur max par champ (par cellule ou chaîne JSON)
- Profondeur max d'imbrication pour JSON
- Limite de temps par parsing
L'encodage est un autre piège fréquent. Privilégiez UTF‑8, gérez un BOM UTF‑8 et rejetez les fichiers qui ne peuvent pas être décodés proprement. Un décodage "best effort" silencieux peut transformer un octet corrompu en délimiteur décalé qui casse toutes les lignes.
Rejetez le contenu inattendu tôt. Pour le CSV, validez l'en‑tête exactement (ou depuis une petite allowlist) avant de traiter les lignes. Pour le JSON, confirmez la forme top‑level (objet vs tableau) et les clés requises avant de toucher au reste.
Loggez les rejets sans stocker le contenu sensible. Sauvegardez les métadonnées et les raisons (taille du fichier, nombre de lignes, première ligne d'erreur, règle qui a échoué), mais évitez de garder l'upload brut.
Se défendre contre l'injection de formule CSV
Le CSV paraît inoffensif, mais les tableurs traitent certaines cellules comme des formules. Si un attaquant uploade un CSV et que vous l'exportez ensuite pour que quelqu'un l'ouvre, une valeur qui commence comme une formule peut s'exécuter. C'est l'injection de formule CSV.
Le problème courant n'est pas que votre parseur plante. Le risque est ce qui arrive après le parsing, quand un humain ouvre les données et que le tableur évalue la formule.
Une règle pratique : conservez la valeur d'origine pour le stockage et l'audit, mais générez une copie sûre pour export pour tout endroit où la valeur pourrait finir dans un CSV ouvert par un tableur. Ainsi vous ne perdez pas ce que l'utilisateur a envoyé et vous n'offrez pas un fichier prêt à être exécuté à votre équipe.
Pour construire la copie d'export sûre, considérez une cellule comme risquée si, après suppression des espaces et tabs initiaux, elle commence par l'un de ces caractères : =, +, - ou @. Si elle est risquée, neutralisez‑la en préfixant une simple quote (') avant la valeur dans le CSV exporté. Beaucoup de tableurs afficheront le texte sans l'exécuter.
Attention aux entrées sournoises. Les attaquants ajoutent souvent des espaces initiaux, des tabs ou des caractères invisibles pour que la cellule semble normale mais qu'elle s'évalue comme formule. Décidez quels caractères initiaux vous considérez comme ignorable (espaces et tabs au minimum) et appliquez la vérification à la version nettoyée.
Testez avec des payloads qui passent souvent à travers :
=HYPERLINK("example","click")+SUM(1,1)-2+3@SUM(1,1)\t=1+1(espace ou tab initial)
Rendre le parsing JSON prévisible avec de la validation
Le JSON fourni par les utilisateurs n'est pas juste des données. C'est une entrée non fiable et vous voulez qu'elle se comporte de la même façon à chaque fois. L'objectif est simple : rejeter les surprises tôt et n'accepter que la forme exacte attendue.
Commencez par valider la structure avant d'utiliser les valeurs : champs requis, types corrects, et un ensemble restreint de valeurs autorisées. Si votre endpoint attend quelque chose comme { "email": string, "role": "admin"|"member" }, n'acceptez pas des nombres, des tableaux ou des objets supplémentaires "parce que ça pourrait quand même marcher." C'est comme ça que des cas limites deviennent des bugs en production.
Imposer des limites strictes sur le JSON coûteux à traiter
Un fichier JSON peut être petit en octets mais coûteux à parser, comme des tableaux profondément imbriqués ou des chaînes énormes. Mettez des limites dès le départ :
- Profondeur maximale d'imbrication
- Longueur maximale de chaîne par champ (en particulier notes, bios et metadata)
- Longueur maximale des tableaux
- Nombre maximal de clés par objet
Ces vérifications rendent le parsing prévisible et protègent la mémoire et le CPU.
Considérez les clés dupliquées comme un problème
Les payloads JSON peuvent contenir des clés dupliquées et les parseurs les gèrent différemment (première gagne, dernière gagne ou indéfini). Cette ambiguïté peut être exploitée pour contourner la validation. Dans la plupart des cas, rejetez les payloads avec clés dupliquées pour empêcher qu'un mauvais valeur soit cachée derrière une bonne.
Évitez aussi le "devinage" de type. Si vous avez besoin d'une chaîne, gardez‑la comme chaîne. Ne convertissez pas automatiquement "00123" en nombre ou n'interprétez pas implicitement des dates. Cela change le sens et peut casser la logique en aval.
Quand la validation échoue, retournez des erreurs claires et sûres pour l'utilisateur comme contacts[12].email must be a valid email address. N'affichez pas de stack traces, de détails SQL ou d'informations internes.
Étape par étape : parsing en streaming qui ne fera pas planter votre appli
Les plantages arrivent généralement parce que le serveur essaie d'être "utile" et charge l'upload entier en mémoire. Le schéma plus sûr est : fixez des limites d'abord, puis traitez un petit morceau à la fois.
Commencez par une vérification rapide avant le parsing. Contrôlez le content type déclaré, mais ne lui faites pas confiance. Appliquez une taille maximale stricte et rejetez les fichiers compressés à moins de pouvoir les inspecter en toute sécurité. Mettez aussi une limite de temps pour qu'un upload lent n'occupe pas indéfiniment un worker.
Un workflow de streaming pratique
Un flux simple qui marche pour les uploads CSV et JSON :
- Pré‑vérifiez les bornes : octets max, encodages autorisés et un max de lignes ou d'objets.
- Stream depuis le disque ou le corps de la requête. N'appelez pas "read all" et ne construisez pas une grande chaîne en mémoire.
- Parsez en record‑par‑record et arrêtez‑vous quand vous atteignez des limites strictes (lignes, champs par ligne, profondeur max JSON, longueur max de chaîne).
- Validez chaque enregistrement au fur et à mesure, et collectez seulement un petit échantillon d'erreurs (par ex. les 20 premières) plus des compteurs.
- Écrivez les données acceptées de façon incrémentale : inserts par lot ou poussez les enregistrements valides sur une queue pour traitement ultérieur.
N'essayez pas de "corriger" une structure cassée pendant le streaming. Si le parseur signale une entrée malformée, stoppez et échouez vite. Les imports partiels sont acceptables uniquement si votre produit les explique clairement.
Pour un import convivial, retournez un résumé : combien d'enregistrements ont été acceptés, combien rejetés, et une courte liste d'exemples d'erreurs avec numéros de ligne (ou chemins JSON). Par exemple : "2 431 importés, 17 rejetés. Problèmes principaux : email manquant, date invalide, colonnes supplémentaires."
Règles de validation qui empêchent les mauvaises données
Le parsing n'est que la première étape. La vraie sécurité et qualité viennent du fait de traiter chaque upload comme non fiable et de le vérifier contre un contrat clair avant qu'il n'atteigne la base.
Écrivez ce que signifie "valide" pour votre appli. Gardez‑le petit et spécifique, et appliquez‑le de la même manière pour les colonnes CSV et les champs JSON. Un bon contrat couvre généralement champs autorisés, requis vs optionnels, règles de types et formats (email, téléphone, date ISO, monnaie), limites de plage et limites de longueur.
Les allowlists importent parce qu'elles empêchent des champs surprises de se faufiler, comme une clé isAdmin inattendue dans le JSON ou une colonne role en trop dans le CSV. Si un en‑tête ou une clé n'est pas sur la liste, rejetez le fichier ou ignorez explicitement le champ et loggez‑le, mais n'acceptez pas silencieusement.
La normalisation doit être prudente et prévisible. Supprimer les espaces et convertir TRUE en true est acceptable, mais évitez les conversions qui changent le sens. Pour les dates, choisissez un format accepté (ou une petite liste), normalisez à une sortie unique et soyez cohérent.
Les messages d'erreur doivent aider l'utilisateur à corriger le fichier rapidement. Plutôt que "entrée invalide", renvoyez quelque chose comme : "Ligne 17, champ email : attendu [email protected]." Pour le JSON, pointez un chemin : "contacts[3].phone manquant."
Décidez dès le départ du niveau de sévérité. Tout ou rien est le plus sûr pour des imports qui doivent être cohérents. Le succès partiel peut être meilleur pour des listes de contacts, mais il doit avoir des règles claires (qu'est‑ce qui est rejeté, combien d'erreurs vous retournez, et ce que vous stockez).
Erreurs et pièges courants à éviter
La plupart des bugs d'upload ne sont pas une grosse vulnérabilité unique. Ce sont une chaîne de petites hypothèses.
Un piège courant est de tout parser d'abord puis d'exécuter les vérifications. Au moment où la validation échoue, les mauvaises données peuvent déjà être en mémoire, écrites dans une table temporaire ou passées dans la logique métier. Traitez la validation comme faisant partie du parsing : rejetez tôt et arrêtez la lecture dès que vous savez que le fichier n'est pas acceptable.
Une autre erreur facile est de faire confiance au nom de fichier. Quelqu'un peut uploader un fichier nommé contacts.csv qui est en réalité autre chose, ou un CSV si malformé que votre parseur se comporte de façon étrange. Inspectez le contenu (headers, délimiteurs, premiers octets) et appliquez une petite forme autorisée avant de vous engager dans le traitement.
Quelques pièges reviennent souvent :
- Laisser une librairie auto‑détecter les types sans limites. Le guessing peut transformer des entrées étranges en nombres énormes, dates ou valeurs
NaN. - Réexporter du CSV brut sans protection contre les formules. Stocker
=HYPERLINK(...)est une chose ; l'exporter pour qu'un collègue l'ouvre en est une autre. - Charger le fichier entier pour "obtenir de meilleures erreurs." Un seul upload surdimensionné peut provoquer des pics de mémoire et des timeouts.
- Retourner des rapports d'erreurs massifs. Lister chaque ligne bad pour un fichier énorme peut créer un second problème mémoire et divulguer des fragments sensibles.
Exemple réaliste : vous importez un "leads.csv" et retournez une erreur détaillée par ligne. Un attaquant upload un gros fichier avec de petites erreurs sur chaque ligne. Votre serveur passe des minutes à collecter les erreurs, construit une réponse de plusieurs mégaoctets et timeoute.
Checklist rapide pour la sécurité des uploads CSV et JSON
Si vous voulez un parsing CSV et JSON sûr, supposez que le fichier est hostile. La plupart des bugs d'upload ne sont pas des hacks sophistiqués. Ce sont des entrées simples que votre code n'attendait pas : fichiers énormes, encodages étranges ou champs qui semblent inoffensifs mais déclenchent des comportements dans d'autres outils.
Gardez une checklist courte comme porte avant que les données n'atteignent votre base :
- Fixez des limites strictes : octets max, lignes/objets max, colonnes/champs max, et (pour JSON) profondeur max d'imbrication.
- Parsez en streaming pour qu'un upload ne puisse pas faire monter la mémoire.
- Validez la structure et les valeurs : champs requis, types, limites de longueur, enums autorisés et formats date/numéro.
- Neutralisez l'injection de formule CSV lors de l'export ou de la ré‑sauvegarde des données.
- Echouez fermé : rejetez sur erreur de parsing et retournez des messages courts et clairs.
Pour le JSON, "du JSON valide" ne suffit pas. Un gros fichier avec des tableaux profondément imbriqués peut être valide et pourtant ruiner la performance. Les limites de profondeur, les longueurs par champ et une validation de schéma stricte rendent le parsing prévisible.
Une fois en production, surveillez les basiques : timeouts de parsing et parsings lents, taux de rejet par raison, et nombre moyen de lignes/objets par upload. Ces signaux vous disent où ajuster vos limites et votre UX.
Exemple réaliste : un import de contacts qui reste sûr
Un fondateur ajoute une étape "Importer des contacts" au flux d'inscription. Les utilisateurs peuvent uploader un CSV depuis Excel ou un export JSON d'un autre outil. Le but est simple : créer des enregistrements de contact (nom, email, entreprise) et ignorer tout ce qui est dangereux.
Un jour, un CSV arrive avec ceci dans la colonne prénom :
=HYPERLINK("example","Click me")
Si votre appli exporte ensuite ces contacts en CSV pour qu'un collègue l'ouvre, cette cellule peut s'exécuter comme une formule. La solution n'est pas de "sanitiser plus tard." Pour tout champ qui pourrait être réécrit dans un CSV, soit rejetez les valeurs qui commencent par =, +, - ou @, soit stockez une version d'export sûre (par ex. préfixer par une apostrophe) et gardez l'original hors des exports.
Un autre jour, quelqu'un upload un gros fichier JSON. Si votre serveur le lit en mémoire et le parse en une fois, il peut geler ou planter. Au lieu de cela, appliquez une limite de taille d'upload, parsez en stream, traitez contact par contact et arrêtez‑vous tôt quand les limites sont dépassées (nombre max d'enregistrements, longueur max d'un champ, profondeur max d'imbrication).
Ce que voit l'utilisateur compte. La page d'import doit fournir un résumé clair : combien ont été importés, combien ont été ignorés, et un petit échantillon de raisons avec numéros de ligne ou chemins JSON. En coulisses, les logs doivent vous aider à déboguer sans stocker de données personnelles. Loggez des comptes, des numéros de ligne, des codes d'erreur et des empreintes non identifiantes pour repérer des récurrences sans conserver les valeurs brutes.
Étapes suivantes : durcir votre pipeline et demander de l'aide si nécessaire
Traitez les uploads comme une intégration externe. Construisez un petit ensemble de garde‑fous que vous pouvez vérifier.
Auditez les endpoints qui acceptent des fichiers. Cherchez des limites claires de taille, des timeouts, du parsing en streaming et des endroits où le fichier est lu deux fois ou converti en une grande chaîne avant parsing.
Gardez un petit pack de tests à lancer à chaque changement : un CSV malformé, un CSV testant l'injection de formule, un JSON profondément imbriqué, un JSON aux mauvais types et champs manquants, et un gros fichier valide proche de votre limite. Ces quelques fichiers attrapent la plupart des régressions.
Si vous travaillez avec un codebase généré par l'IA (surtout des prototypes venant d'outils comme Lovable, Bolt, v0, Cursor ou Replit), faites une passe supplémentaire pour chercher les limites manquantes, les lectures de fichier entières et le logging brut. Si vous voulez un second avis, FixMyMess (fixmymess.ai) aide les équipes à diagnostiquer et réparer les flux d'import générés par l'IA, en ajoutant des frontières, de la validation et un traitement d'export plus sûr.
Questions Fréquentes
Pourquoi les uploads CSV et JSON sont-ils risqués alors que c’est « juste du texte » ?
Traitez chaque upload comme une entrée non fiable. Même du “simple texte” peut déclencher des cas limites du parseur, créer des enregistrements incorrects ou causer des problèmes plus tard quand les données sont exportées et ouvertes dans un tableur.
Dois‑je essayer de « corriger automatiquement » les fichiers CSV mal formés ?
Un parsing strict évite des déplacements silencieux de colonnes. Si vous tentez de deviner ce que l'utilisateur « voulait dire », une quote non fermée ou une virgule en trop peut déplacer des valeurs dans de mauvaises colonnes et corrompre votre base sans avertissement.
Quelles limites devrais‑je appliquer avant d'analyser un fichier uploadé ?
Définissez des limites avant le parsing : octets maximum, lignes/colonnes maximum et longueur maximale des champs. La taille seule ne suffit pas, car un fichier modeste peut contenir des champs gigantesques ou s'étendre beaucoup une fois parsé.
Comment éviter que des uploads ne plantent mon appli à cause d'explosions de mémoire ?
Stream‑ez et validez au fur et à mesure. Si vous lisez tout le fichier en mémoire d'abord, un upload volumineux (ou un petit fichier qui s'étend pendant le parsing) peut faire exploser la mémoire et planter le worker ou provoquer des timeouts.
Qu'est‑ce que l'injection de formule CSV, et quand est‑ce que ça compte vraiment ?
C’est quand une valeur commençant par =, +, - ou @ est exportée puis ouverte dans Excel ou Google Sheets, où elle peut être évaluée comme formule. Le risque apparaît souvent plus tard dans le flux humain, pas pendant l'import lui‑même.
Quelle est la façon la plus simple et sûre de gérer l'injection de formule CSV à l'export ?
Conservez la valeur d'origine pour le stockage, mais générez une version sûre pour tout CSV destiné à être ouvert dans un tableur. Une approche courante : après avoir supprimé les espaces/tabs initiaux, préfixer par une apostrophe (') si la valeur commence par un caractère de formule.
Comment rendre le parsing JSON prévisible et sûr ?
Validez la forme exacte attendue avant d'utiliser les valeurs, et rejetez les surprises tôt. Mettez des plafonds stricts sur la profondeur de nesting, la longueur des chaînes, la taille des tableaux et le nombre total de clés pour que du « JSON valide » ne soit pas néanmoins coûteux à traiter.
Dois‑je rejeter le JSON avec des clés dupliquées ?
Comme les parseurs gèrent différemment les clés dupliquées (première gagne, dernière gagne ou comportement indéfini), cette ambiguïté peut être exploitée pour contourner la validation. Par défaut, le plus sûr est de rejeter les objets JSON contenant des clés dupliquées.
Quels messages d'erreur devrais‑je afficher quand un import échoue la validation ?
Retournez un résumé clair et court que l'utilisateur peut exploiter, par exemple quel row/path a échoué et pourquoi. Évitez d'échoer du contenu sensible brut, des traces de stack ou des rapports ligne par ligne énormes qui deviennent un second problème de performance.
Quelles sont les erreurs les plus courantes dans le code d'import généré par l'IA, et comment FixMyMess peut‑il aider ?
Cherchez les lectures de fichier intégral, l'absence de limites de taille/temps, le logging brut du contenu uploadé et les exports qui ré‑sauvent des données utilisateur en CSV sans protection. Si le code d'import a été généré par une IA et est instable en production, FixMyMess peut l'auditer rapidement et réparer les limites, le streaming, la validation et le traitement d'export pour qu'il se comporte de façon prévisible.