Les points à surveiller en sécurité — ne pas faire confiance aux entrées, protéger les secrets

Cet article fait partie du cours Les bases de l'informatique, qui construit depuis zéro les connaissances informatiques pratiques indispensables pour programmer et faire du vibe coding.
Deux principes de base : ne pas faire confiance aux entrées de l'utilisateur, et protéger les informations secrètes. Des schémas montrent comment surviennent l'injection SQL et le XSS.

Cet article traite deux des points à surveiller en sécurité.

Le premier : ne pas utiliser tel quel le texte écrit par un utilisateur. Le second : ne pas placer de valeurs secrètes là où davantage de personnes peuvent les lire.

  • Pourquoi la validation des entrées se fait côté serveur
  • L'injection SQL, qui survient quand une entrée est concaténée à une instruction, et les placeholders
  • Le XSS, évité par l'échappement juste avant l'affichage à l'écran
  • Où tu peux conserver les informations secrètes comme les clés d'API et les mots de passe

Si l'un de ces problèmes survient une fois le site en ligne, on ne peut pas revenir en arrière : les données déjà lues sont irrécupérables.

« Il n'y a pas de validation des entrées » signifie qu'une valeur est utilisée telle qu'elle arrive

La validation des entrées (input validation) consiste à vérifier, avant de l'utiliser, qu'une valeur reçue respecte le format, la longueur et l'intervalle attendus.

Tu fixes une condition par champ : le nom jusqu'à 50 caractères, le nombre d'étoiles de 1 à 5.

Valeur reçueCondition fixée à l'avanceCe que fait le serveur
Champ nom : AliceJusqu'à 50 caractères → conformePasse à l'enregistrement
Nombre d'étoiles : 7Un nombre de 1 à 5 → non conformeRefusé et renvoyé sans enregistrement
Corps du message : laissé videAu moins un caractère → non conformeRefusé et renvoyé sans enregistrement

Seule la première ligne remplit la condition et passe à l'enregistrement ; les deux autres ne sont pas enregistrées et le serveur renvoie ce qui ne convient pas.

Le contrôle à l'écran est exécuté par le JavaScript (un programme qui tourne dans le navigateur) livré à l'appareil de l'utilisateur.

L'utilisateur peut le désactiver, et il peut aussi envoyer les données directement au serveur sans passer par un navigateur.

Le même 7 emprunte deux chemins selon la façon dont il est envoyé
Envoyer 7 commenote en étoilesEnvoyer depuisle formulaireCouper le contrôleet envoyerBloqué par lecontrôle à l'écranArrive au serveursans contrôleRejeté par lavalidation serveur
La valeur unique de gauche part vers le haut ou vers le bas selon la façon dont elle est envoyée. Le chemin du bas ne passe pas par le contrôle à l'écran : sans validation côté serveur, elle est enregistrée telle quelle.

Le contrôle à l'écran sert à faire corriger la saisie, et seule la validation côté serveur décide si une valeur peut être utilisée.

Si tu enregistres sans validation, les valeurs non conformes restent telles quelles dans la base de données.

Des nombres d'étoiles en dehors de 1 à 5 faussent les moyennes et le tri, et des valeurs qui ne se lisent pas comme des nombres interrompent le calcul des totaux.

Corriger les valeurs déjà entrées suppose d'examiner les données restantes une par une.

Vérifie la valeur reçue sur le serveur avant de l'utiliser

La validation des entrées consiste à regarder, avant utilisation, si une valeur reçue remplit les conditions fixées.

Le contrôle à l'écran sert à faire corriger la saisie sur le moment et peut être désactivé avant l'envoi : tu vérifies donc la même chose une seconde fois côté serveur.

Concaténé à une instruction, le texte saisi s'exécute comme une instruction

Pour retrouver par nom les avis enregistrés, le serveur assemble une instruction en SQL (Structured Query Language, le langage qui transmet les instructions à une base de données).

Quand le texte saisi est concaténé à cette chaîne, l'ensemble est transmis comme une seule chaîne.

Ce que contient la chaîne unique transmise à la base de données
La chaîne unique que reçoit la base de données
La partie écrite par le développeur
  • SELECT * FROM reviews WHERE name =
  • Une instruction qui extrait de la table reviews les lignes dont le name correspond
  • Cette forme est écrite à l'avance dans le code
La partie venue du champ nom
  • Alice — un nom ordinaire
  • ' OR '1'='1 — du texte contenant des symboles
  • Le texte saisi vient se placer tel quel à cet endroit
Le cadre extérieur est la chaîne unique transmise du serveur à la base de données. Une fois transmises, les deux parties qu'elle contient ne se distinguent plus.

Une fois transmises, la partie écrite par le développeur et celle venue du champ nom font partie d'une seule et même chaîne.

La base de données lit la chaîne qu'elle reçoit comme une instruction, dans son intégralité.

Texte saisi dans le champ nomInstruction produite par la concaténationCe que renvoie la base de données
Alicename = 'Alice'Uniquement les avis d'Alice
Alice'name = 'Alice''L'instruction est mal formée et provoque une erreur
' OR '1'='1name = '' OR '1'='1'Tous les avis

Le fait qu'un texte saisi soit exécuté comme une instruction au lieu d'être traité comme une valeur est une injection, et celle qui survient dans une base de données est une injection SQL (SQL injection).

L'écriture qui l'empêche est le placeholder : dans l'instruction, un symbole marque seulement l'emplacement de la valeur, et la valeur est transmise séparément.

La même entrée se sépare en deux selon la façon dont le code du serveur est écrit
Champ nom :' OR '1'='1Concaténé àl'instructionInstruction etvaleur séparéesLit une chaînecomme instructionCherche la valeurcomme un nomTous les avissont renvoyésAucun avis trouvé
L'entrée unique de gauche part vers le haut ou vers le bas selon l'écriture. Quand l'instruction et la valeur sont transmises séparément, même un texte contenant des symboles est traité comme un nom.

Quand ils sont transmis séparément, la base de données ne lit pas la valeur comme une instruction : elle cherche ce texte comme un nom et ne trouve aucun résultat.

La validation vérifie le format d'une valeur, tandis que le placeholder fait traiter l'entrée comme une valeur, quel que soit son format.

Les rôles sont différents, donc tu fais les deux.

Ne pas réunir l'instruction et le texte saisi en une seule chaîne

Quand un texte saisi est concaténé à une instruction, même les symboles saisis sont lus comme une partie de l'instruction.

Transmettre l'instruction et la valeur séparément fait qu'un texte contenant des symboles est traité comme un simple nom ; tu gardes donc les deux : la validation qui vérifie le format, et l'écriture qui fait traiter la valeur comme une valeur quel que soit son format.

Sans transformation juste avant l'affichage, une publication s'exécute dans le navigateur d'un autre utilisateur

La même chose se produit à l'endroit où une publication est affichée à l'écran.

Le fait qu'un texte saisi s'exécute comme du JavaScript dans le navigateur d'un autre utilisateur est le XSS (Cross-Site Scripting).

La même entrée reçoit deux noms selon ce à quoi elle est mêlée
Texte écrit parl'utilisateurMêlé à uneinstruction SQLLa base le litcomme instructionInstruction etvaleur séparéesMêlé au HTMLde la pageLu par un autrenavigateurÉchapper justeavant l'affichage
À gauche, ce que l'utilisateur a écrit. Le chemin du haut est celui où le texte est mêlé à une instruction SQL, celui du bas où il est mêlé au HTML de la page. Ce qui le lit diffère, donc le remède diffère aussi.

Dans les deux cas, l'entrée est lue comme une instruction ; seul ce qui la lit change.

Le remède du côté de l'affichage est l'échappement (escape), c'est-à-dire la conversion des caractères qui ont un sens particulier en une forme qui s'affiche comme de simples caractères.

Le point de bascule n'est pas le moment de l'enregistrement, mais juste avant l'affichage à l'écran.

Caractère au sens particulier en HTMLÉcriture après remplacementCaractère visible à l'écran
<&lt;<
>&gt;>
&&amp;&
"&quot;"
'&#x27;'

Remplacés juste avant l'affichage, les messages restent du texte

Si tu affiches une publication telle quelle, ce qui y était mêlé s'exécute dans le navigateur d'un autre utilisateur.

Ce qui arrête cela, c'est le remplacement effectué juste avant l'affichage ; comme seuls les symboles sont remplacés, le texte que voit le lecteur ne change pas.

Arrêter les valeurs secrètes avant qu'elles ne deviennent lisibles par plus de personnes

L'autre point concerne l'endroit où sont conservées les informations secrètes.

Ce que tu regardes ici, c'est combien de personnes peuvent lire l'endroit où tu les as placées.

Jusqu'où voyagent les fichiers de my-app
Dossier de travail sur ton ordinateur
Non enregistré dans Git (reste seulement chez toi)
  • .env — les valeurs des clés d'API et des mots de passe
  • Fichiers de configuration utilisés seulement chez toi
Enregistré dans Git (parvient à toutes les personnes à qui tu donnes le code)
  • server.js — le code qui tourne sur le serveur
  • .gitignore — la liste des fichiers à ne pas enregistrer
  • .env.example — un modèle qui ne contient que les noms
Livré au navigateur (lisible par toute personne qui ouvre l'URL)
  • index.html — l'écran des avis
  • script.js — le code qui tourne sur la page
Le cadre extérieur est le dossier de travail sur ton ordinateur. Plus un fichier se trouve dans un cadre intérieur, plus de personnes peuvent le lire : tu n'écris donc pas de valeurs secrètes dans les cadres intérieurs.

Plus le cadre est intérieur, plus il y a de lecteurs : script.js est lisible par n'importe qui, et server.js, une fois enregistré dans Git, est lisible par toutes les personnes à qui tu as donné le code.

Écrire .env dans le .gitignore (un fichier qui liste les noms des fichiers que Git ne doit pas enregistrer) empêche les valeurs d'entrer dans l'historique, mais

une fois enregistrées, elles ne disparaissent plus de l'historique : tu régénères donc les valeurs elles-mêmes.

Jusqu'où voyage sur GitHub un fichier enregistré dans Git
N'enregistrerque le codeDépôt publicsur GitHubLisible parn'importe qui.env aussienregistréLe fichier restedans l'historiqueLisible mêmeaprès effacementL'écrire dans.gitignoreN'entre pas dansl'historiqueReste seulementchez toi
La ligne du haut est le code placé dans un dépôt public, celle du milieu le cas où .env a été enregistré lui aussi, et celle du bas le cas où il a été écrit dans .gitignore. De gauche à droite, l'endroit où le fichier est placé détermine qui peut le lire.

Une valeur entrée dans un dépôt public est lisible par n'importe qui dans le monde.

L'endroit où est conservée une clé d'API et le cercle des personnes qui peuvent la lire
Lisible par toute personne qui ouvre l'URL
  • script.js — le code livré au navigateur
  • La fin de l'URL que tu appelles
  • Les messages d'erreur affichés à l'écran
Lisible par toutes les personnes à qui tu donnes le code
  • server.js — le code enregistré dans Git
  • .env une fois enregistré — reste dans l'historique
Lisible seulement par les personnes qui ont accès au serveur
  • Variables d'environnement du serveur — les clés d'API et les mots de passe vont ici
Plus le cercle est extérieur, plus il y a de lecteurs. Les seuls endroits où tu peux placer une valeur sont ceux du cercle le plus intérieur.

Le cercle le plus intérieur ne contient que les variables d'environnement du serveur.

Tu n'écris pas les valeurs dans les endroits des deux cercles extérieurs, ni dans les logs (l'enregistrement de l'activité que garde le serveur), qui peuvent sortir du serveur.

Le my-app qui tourne dans le cadre de droite charge la valeur depuis les variables d'environnement.

Cette valeur n'entre jamais dans les deux cadres de gauche : le nombre de personnes qui peuvent la lire n'augmente pas.

L'endroit où placer un secret dépend du nombre de lecteurs

L'endroit où tu peux conserver une valeur secrète dépend du nombre de personnes qui peuvent lire cet endroit.

Un fichier livré au navigateur est lisible par n'importe qui et un fichier enregistré dans Git parvient à toutes les personnes à qui tu donnes le code : tu n'écris la valeur ni dans l'un ni dans l'autre, tu la places dans les variables d'environnement du serveur.

QUIZ

Vérification des connaissances

Répondez à chaque question une par une.

Question 1Pourquoi valider aussi côté serveur, alors que les valeurs du formulaire sont déjà contrôlées à l'écran ?

Question 2Que fait l'écriture avec placeholder, qui empêche l'injection SQL ?

Question 3Quel est le bon endroit pour conserver la clé d'API d'un service externe ?