JavaScript et SQL côté web, ce qu'il faut savoir des deux langages
- Accueil
- Développement, applications & chaîne logicielle
- JavaScript et SQL côté web, ce qu'il faut savoir des deux langages
JavaScript et SQL côté web, ce qu’il faut savoir des deux langages
Section intitulée « JavaScript et SQL côté web, ce qu’il faut savoir des deux langages »Une application web parle au moins deux langages : JavaScript, qui s’exécute dans le navigateur et souvent sur le serveur, et SQL (Structured Query Language), qui porte les échanges entre l’application et la base de données. Lire un code web, relire une revue de sécurité ou trier un rapport de scan demande moins de syntaxe que de modèle : qui exécute quoi, avec quels pouvoirs, et à quel moment une donnée devient du code. La position défendue ici tient en une ligne : la sécurité d’une application web se joue aux frontières entre ces deux langages, pas à l’intérieur de l’un d’eux.
Dans un restaurant, la personne en salle prend la commande et la cuisine la prépare à partir du garde-manger. Deux métiers, deux façons de parler, et un client qui ne voit jamais les fourneaux. Un site web tourne avec deux langues de ce genre : savoir qui dit quoi à qui évite de servir n’importe quoi à n’importe qui.
→ Lire l’explication complète de la marmotte
JavaScript : un langage normalisé, deux environnements d’exécution
Section intitulée « JavaScript : un langage normalisé, deux environnements d’exécution »JavaScript est normalisé sous le nom ECMAScript par la norme ECMA-262, tenue par le comité TC39 (Technical Committee 39) d’Ecma International, avec une édition par an depuis 2015. La norme décrit le langage : syntaxe, types, objets, promesses. Elle ne dit rien de ce qui l’entoure. Ni la page, ni le réseau, ni les fichiers n’en font partie : ce sont des API (Application Programming Interface) fournies par l’environnement d’exécution. D’où la première question à poser devant un fichier JavaScript : où tourne-t-il ? L’histoire du langage et sa place dans la frise générale sont traitées dans l’histoire des langages informatiques.
Dans le navigateur, le code s’exécute dans un bac à sable. Il manipule la page à travers le DOM (Document Object Model, la représentation en arbre du document affiché) et reste soumis à la politique de même origine : une origine est le triplet schéma, hôte, port, et un script ne lit pas la réponse d’une autre origine sans autorisation explicite du serveur visé. Nuance qui coûte cher quand elle est oubliée : pour les requêtes qu’un simple formulaire sait émettre, cette politique bloque la lecture de la réponse, pas l’envoi de la requête. Le serveur l’a donc reçue et traitée, cookies de session compris si leur configuration le permet.
Sur le serveur, avec Node.js, le même langage tourne avec les droits du compte système qui a lancé le processus : fichiers, connexions sortantes, lancement de commandes. Un modèle de permissions existe dans les versions récentes, mais il s’active explicitement ; par défaut, rien n’est cloisonné.
| Question | Navigateur | Serveur (Node.js) |
|---|---|---|
| Qui contrôle l’exécution | L’utilisateur, donc potentiellement l’attaquant | L’exploitant |
| Pouvoirs | Page courante, stockage de l’origine, requêtes réseau encadrées | Fichiers, réseau, processus, variables d’environnement |
| Secrets | Aucun ne tient : tout le code livré est lisible | Possibles, à condition de ne jamais les renvoyer |
| Effet d’une chaîne exécutée comme code | Vol de session, actions au nom de la victime | Exécution de commandes sur la machine |
| Valeur de ses contrôles pour le reste du système | Nulle | Réelle, si le contrôle est fait à chaque requête |
Le schéma suivant montre ce que chaque environnement accorde au même code.
Le compromis central est là. Un seul langage des deux côtés, c’est une équipe, un outillage, du code partagé. Le prix : l’illusion qu’un module de validation partagé produit la même garantie partout. Le même fichier exécuté dans le navigateur est une suggestion ; exécuté sur le serveur, c’est un contrôle. La recommandation est de partager le code si on veut, jamais la confiance.
Typage dynamique, prototypes et chaînes transformées en code
Section intitulée « Typage dynamique, prototypes et chaînes transformées en code »Trois traits du langage expliquent la majorité des constats d’une revue de sécurité JavaScript.
Le typage dynamique et les conversions implicites. Une variable n’a pas de type, une valeur en a un, et l’opérateur == convertit ses opérandes avant de comparer. L’opérateur === compare sans convertir.
0 == "" // true : la chaîne vide est convertie en nombre"0" == false // true : les deux côtés finissent en 0null == undefined // true0 === "" // false : types différents, aucune conversionNaN === NaN // false : NaN n'est égal à rien, même à lui-même[10, 9, 1].sort() // [1, 10, 9] : le tri par défaut compare des chaînesLa conséquence pour un serveur dépasse le cas d’école. Un corps de requête en JSON (JavaScript Object Notation) peut livrer un objet ou un tableau là où le code attendait une chaîne, et rien ne proteste. Selon la bibliothèque d’accès aux données, cet objet peut être interprété comme un opérateur de filtre au lieu d’une valeur. Règle : === partout, et vérification du type réel de chaque champ reçu.
Le modèle objet à prototypes. JavaScript n’a pas de classes au sens strict ; le mot-clé class habille un mécanisme plus ancien. Chaque objet pointe vers un prototype, et la lecture d’une propriété absente remonte cette chaîne jusqu’à Object.prototype, que presque tous les objets partagent. Or ces objets sont modifiables à l’exécution, prototype commun compris. Une fusion récursive qui recopie des clés fournies par l’utilisateur (__proto__, constructor, prototype) peut donc modifier le comportement de tous les objets du processus. C’est le trait qui rend possible la pollution de prototype, dont l’exploitation est décrite dans la démonstration de pollution de prototype. Les parades tiennent au langage : objets créés sans prototype (Object.create(null)), Map pour les dictionnaires indexés par une saisie, refus explicite des trois clés lors d’une fusion.
Les fonctions qui transforment une chaîne en code. eval, le constructeur Function, setTimeout appelé avec une chaîne : tous exécutent du texte. Dans le navigateur, innerHTML et ses cousins font la même chose avec du HTML (HyperText Markup Language) : la chaîne est analysée comme du balisage, gestionnaires d’événements compris. Une balise script insérée ainsi ne s’exécute pas, un attribut onerror sur une image, si. C’est le trait derrière le XSS (Cross-Site Scripting), dont le mécanisme, les variantes et les défenses ont leur page. La règle de relecture : toute occurrence de ces fonctions se justifie ou disparaît, et l’affichage d’une donnée passe par textContent ou par un moteur de gabarits qui échappe par défaut. Côté serveur, le module vm de Node.js n’est pas un mécanisme de sécurité, sa documentation le dit sans détour.
TypeScript ajoute des types vérifiés à la compilation, puis les efface. À l’exécution, il ne reste que du JavaScript : aucune vérification. Un as Utilisateur posé sur le résultat d’un JSON.parse est une promesse faite au compilateur, pas un contrôle. Un champ « âge » déclaré comme nombre finit tôt ou tard par recevoir un tableau, et le compilateur n’est plus là pour le voir. TypeScript réduit les erreurs internes au code ; ce qui franchit une frontière (requête, fichier, réponse de base) se valide à l’exécution avec un schéma.
Boucle d’événements, asynchronisme et modules npm
Section intitulée « Boucle d’événements, asynchronisme et modules npm »Un processus JavaScript exécute le code sur un fil unique, piloté par une boucle d’événements. Les entrées-sorties (réseau, disque, base) sont déléguées au système ; leur résultat revient sous forme de promesse, et async/await n’est qu’une syntaxe plus lisible par-dessus. Ce modèle encaisse un grand nombre de connexions en attente avec peu de mémoire. Il a quatre conséquences à connaître.
- Un calcul long bloque tout le processus serveur, donc tous les clients. Candidats habituels : analyse d’un corps JSON énorme, fonctions synchrones de fichiers ou de cryptographie, expression régulière à retour arrière catastrophique, dite ReDoS (Regular expression Denial of Service). Une requête bien choisie suffit ; pas besoin de botnet. Parades : taille maximale des corps, variantes asynchrones, fils de travail pour le calcul.
- Un
awaitoublié change le sens du code. Une fonction asynchrone renvoie une promesse, et une promesse vaut toujours « vrai » dans unif. Un contrôle d’accès écritif (aLeDroit(utilisateur))sansawaitlaisse passer tout le monde, avec une syntaxe irréprochable. - Le fil unique ne supprime pas les courses. Chaque
awaitrend la main : deux requêtes s’entrelacent entre la lecture et l’écriture, et plusieurs instances tournent de toute façon en parallèle. La cohérence se règle dans la base (voir les niveaux d’isolation plus bas). - Dans les versions actuelles de Node.js, un rejet de promesse non géré termine le processus par défaut. Une erreur non rattrapée dans un chemin secondaire devient une interruption de service.
Le code s’organise en modules, sous deux formats qui cohabitent : CommonJS (require) et ESM (ECMAScript Modules, import). L’écosystème passe par npm, le gestionnaire de paquets de Node.js, et un paquet importé s’exécute avec les droits complets de l’application. Les scripts d’installation d’un paquet tournent par défaut sur le poste et dans la chaîne d’intégration, avant qu’une seule ligne ne soit appelée. Le sujet est traité dans les dépendances transitives et la confiance dans les mises à jour automatiques.
SQL : langage déclaratif, norme ISO/IEC 9075 et dialectes
Section intitulée « SQL : langage déclaratif, norme ISO/IEC 9075 et dialectes »SQL est déclaratif : la requête décrit le résultat voulu, et l’optimiseur du moteur choisit le plan d’exécution (ordre des jointures, index utilisés, méthode de tri). Deux requêtes équivalentes peuvent coûter un millième de seconde ou plusieurs minutes selon les index et les statistiques. Lire le plan (EXPLAIN dans la plupart des moteurs) fait partie de la lecture du code.
La norme ISO/IEC 9075 (Organisation internationale de normalisation et Commission électrotechnique internationale) est révisée régulièrement, et aucun moteur ne l’implémente en entier ni sans extensions.
| Moteur | Identifiants entre | Pagination | Trait à connaître en relecture |
|---|---|---|---|
| PostgreSQL | guillemets doubles | LIMIT, FETCH FIRST | paramètres notés $1, $2 |
| MySQL, MariaDB | accents graves | LIMIT | paramètres notés ?, commentaire # en plus de -- |
| SQL Server | crochets | TOP, OFFSET ... FETCH | concaténation par +, paramètres @nom |
| Oracle | guillemets doubles | FETCH FIRST (versions récentes), ROWNUM | une chaîne vide est traitée comme NULL |
| SQLite | guillemets doubles | LIMIT | typage souple, ni comptes ni GRANT : les droits sont ceux du fichier |
Ces écarts ont une conséquence directe : les règles d’échappement, les commentaires et les fonctions diffèrent d’un moteur à l’autre, donc un échappement écrit à la main est faux quelque part. C’est aussi pourquoi un rapport de scan nomme le moteur visé par chaque charge.
Les instructions se rangent en quatre familles, par usage plus que par norme : DML (Data Manipulation Language : SELECT, INSERT, UPDATE, DELETE), DDL (Data Definition Language : CREATE, ALTER, DROP), DCL (Data Control Language : GRANT, REVOKE) et TCL (Transaction Control Language : BEGIN, COMMIT, ROLLBACK). Ce découpage sert surtout à décider des droits : une application en service n’a besoin que de la première et de la dernière.
SQL raisonne en logique à trois valeurs : vrai, faux, inconnu. NULL signifie « valeur absente », toute comparaison avec lui donne « inconnu », et un WHERE ne garde que le vrai.
-- Syntaxe PostgreSQLSELECT 1 WHERE NULL = NULL; -- aucune ligne : résultat « inconnu »SELECT 1 WHERE NULL IS NULL; -- une ligneSELECT 1 WHERE 3 NOT IN (1, 2, NULL); -- aucune ligne : 3 <> NULL est « inconnu »Le troisième cas est le plus sournois : un NOT IN alimenté par une sous-requête qui renvoie un seul NULL ne retourne plus rien, y compris quand il sert de filtre d’exclusion dans un contrôle d’accès. NOT EXISTS n’a pas ce défaut.
Côté index : une fonction appliquée à la colonne filtrée, une conversion implicite de type ou un LIKE commençant par % peuvent empêcher l’usage d’un index classique. Une requête lente ne bloque pas la boucle d’événements de Node.js (c’est une entrée-sortie), mais elle retient une connexion du pool ; pool épuisé, toutes les requêtes font la queue. La base « tient la charge » sur la diapositive, le pool de dix connexions un peu moins.
Transactions SQL, propriétés ACID et niveaux d’isolation
Section intitulée « Transactions SQL, propriétés ACID et niveaux d’isolation »Une transaction regroupe des instructions avec les propriétés ACID (Atomicité, Cohérence, Isolation, Durabilité) : tout ou rien, contraintes respectées, effets des transactions concurrentes maîtrisés, résultat conservé après validation. L’isolation est la seule qui se règle, et son réglage par défaut n’est presque jamais le plus strict.
| Niveau (norme) | Lecture sale | Lecture non reproductible | Lecture fantôme |
|---|---|---|---|
READ UNCOMMITTED | possible | possible | possible |
READ COMMITTED | non | possible | possible |
REPEATABLE READ | non | non | possible selon la norme |
SERIALIZABLE | non | non | non |
PostgreSQL démarre en READ COMMITTED, MySQL avec InnoDB en REPEATABLE READ. Dans les deux cas, le motif « lire, vérifier, écrire » (solde, stock, quota, coupon à usage unique) n’est pas protégé par la seule présence d’une transaction : deux requêtes concurrentes lisent la même valeur, passent la vérification, écrivent toutes les deux. Quatre réponses, par ordre de préférence :
- une écriture atomique conditionnelle (
UPDATE ... SET stock = stock - 1 WHERE stock >= 1), puis contrôle du nombre de lignes modifiées ; - une contrainte en base (unicité,
CHECK), qui tient même quand le code se trompe ; - un verrou explicite (
SELECT ... FOR UPDATE) ; - le niveau
SERIALIZABLE, qui impose de rejouer la transaction quand le moteur la refuse.
Deux pièges propres au couple Node.js et SQL. Une transaction vit sur une connexion : envoyer BEGIN puis les instructions suivantes à travers un pool, sans réserver une connexion, les répartit sur des connexions différentes et la transaction n’existe plus. Et un await vers un service externe au milieu d’une transaction garde les verrous ouverts le temps de l’appel.
Requête préparée, paramètres liés, ORM et moindre privilège
Section intitulée « Requête préparée, paramètres liés, ORM et moindre privilège »Le défaut d’origine de SQL côté application tient à ceci : la requête est une chaîne de texte. Si la saisie est collée dedans, le moteur reçoit un seul texte et ne peut pas distinguer le code de la donnée. Aucun filtrage ne répare cette confusion ; il faut la supprimer. La requête préparée le fait : le texte, avec des marqueurs, part d’un côté, les valeurs de l’autre, et une valeur liée n’est jamais analysée comme du SQL.
Les limites sont à connaître par cœur, car c’est là que les injections survivent :
- un paramètre lié remplace une valeur, jamais un nom de table, un nom de colonne, un mot-clé ou le sens d’un
ORDER BY. Ces éléments se choisissent dans une liste fermée côté serveur ; - une liste
INde longueur variable demande autant de marqueurs que de valeurs, ou un paramètre de type tableau quand le moteur le permet ; - dans un
LIKE, les caractères%et_d’une valeur liée restent des jokers ; - certains pilotes émulent la préparation côté client par échappement : la séparation n’est alors plus garantie par le protocole. À vérifier dans la documentation du pilote ;
- une donnée stockée proprement puis recollée plus tard dans une requête par un autre traitement produit une injection dite de second ordre. La règle vaut pour toute donnée, pas seulement pour celles qui arrivent du réseau.
Un ORM (Object-Relational Mapping) fait correspondre tables et objets et produit des requêtes paramétrées pour les opérations courantes. Il ne protège pas les requêtes brutes qu’il autorise, ni l’affectation en masse (un corps de requête recopié tel quel dans un objet, champ role inclus), et il fabrique le problème N+1 : une requête pour la liste, puis une par élément. Le choix entre ORM et SQL écrit à la main dépend d’une question : qui, dans l’équipe, lit les requêtes réellement émises ? Si personne, l’ORM masque le problème sans le résoudre ; dans les deux cas, journaliser les requêtes en préproduction et les lire.
Dernier étage : le compte de base utilisé par l’application. Lecture et écriture sur ses tables, rien d’autre. Ni propriété du schéma, ni instructions de définition, ni droits d’administration, ni accès fichier ; les migrations passent par un compte distinct. Une injection hérite exactement des droits du compte : c’est la seule limite qui tient quand tout le reste a cédé. L’exploitation par canal temporel est décrite dans l’injection SQL aveugle temporelle.
Trajet d’une donnée entre JavaScript et SQL : frontières de confiance
Section intitulée « Trajet d’une donnée entre JavaScript et SQL : frontières de confiance »Le cœur du sujet est le trajet complet d’une saisie, du navigateur à la base et retour. Le schéma se lit en comptant les frontières : à chacune, ce qui arrive est traité comme hostile, quelle que soit sa provenance supposée.
Frontière 1. Rien de ce que fait le JavaScript du navigateur n’est une garantie pour le serveur. La requête HTTP (HyperText Transfer Protocol) peut être forgée sans navigateur, le code livré peut être modifié, le bouton grisé se dégrise. La validation côté client est du confort d’usage ; le contrôle est fait par le serveur, à chaque requête, sur le type, la forme et le droit d’agir sur l’objet visé. Ce dernier point est détaillé dans le contrôle d’accès au niveau objet.
Frontière 2. Le texte de la requête ne dépend jamais de la saisie. Les types changent aussi de monde ici : un nombre JavaScript est un flottant double précision selon la norme IEEE 754 (Institute of Electrical and Electronics Engineers), exact pour les entiers jusqu’à 2^53 - 1 seulement. Un identifiant entier sur 64 bits ou un montant décimal n’y tient pas sans perte ; les pilotes sérieux les rendent sous forme de chaîne, et les convertir « pour faire propre » fabrique des identifiants faux.
Frontière 3. Le retour est aussi dangereux que l’aller. Une donnée sortie de la base n’est pas devenue fiable en y séjournant : affichée comme balisage, elle s’exécute chez chaque visiteur. L’échappement se fait à la sortie, selon le contexte d’affichage, pas à l’entrée.
Exemple : requête SQL concaténée face à requête paramétrée en JavaScript
Section intitulée « Exemple : requête SQL concaténée face à requête paramétrée en JavaScript »L’extrait suivant utilise le pilote PostgreSQL pg pour Node.js. Il montre les trois cas rencontrés en relecture : la concaténation, la version paramétrée, et le tri dynamique que les paramètres liés ne couvrent pas.
// Dans un module ESM ; pool est une instance de Pool du paquet "pg".
// À proscrire : la saisie est collée dans le texte de la requête.const fautive = "SELECT id, nom FROM clients WHERE email = '" + email + "'";await pool.query(fautive);
// Attendu : texte fixe, valeur transmise à part ($1 = premier paramètre lié).await pool.query("SELECT id, nom FROM clients WHERE email = $1", [email]);
// Tri dynamique : un nom de colonne ne se lie pas, il se choisit dans une liste fermée.const colonnes = new Map([["nom", "nom"], ["creation", "date_creation"]]);const tri = colonnes.get(choix) ?? "nom";await pool.query(`SELECT id, nom FROM clients ORDER BY ${tri} LIMIT $1`, [50]);Le choix d’une Map plutôt que d’un objet littéral n’est pas cosmétique. Avec { nom: "nom" }, la clé constructor fournie par l’utilisateur ne renvoie pas undefined : elle remonte la chaîne de prototypes et renvoie une fonction, qui finit dans le texte de la requête. Les deux langages se rencontrent dans ces trois dernières lignes, avec leurs deux modèles.
Surface d’attaque, limites et conditions d’exploitation
Section intitulée « Surface d’attaque, limites et conditions d’exploitation »Chaque trait décrit plus haut ouvre une classe d’attaque connue. Le tableau sert de grille de lecture pour un rapport de scan ; la méthode de tri est dans lire un rapport de scan avec l’OWASP (Open Worldwide Application Security Project) Top 10 et ce qu’un outil ne voit pas dans la revue de code au-delà du linter.
| Trait du langage | Attaque rendue possible | Contre-mesure |
|---|---|---|
| Conversions implicites, typage dynamique | Contournement de comparaison, objet reçu à la place d’une chaîne | ===, validation par schéma à l’exécution côté serveur |
| Prototypes partagés et modifiables | Pollution de prototype | Objets sans prototype, Map, refus de __proto__, constructor, prototype |
| Chaîne transformée en code | XSS dans le navigateur, exécution de code sur le serveur | textContent, gabarits à échappement, CSP (Content Security Policy) sans 'unsafe-inline' ni 'unsafe-eval' |
| Fil unique | Déni de service par calcul long, ReDoS | Limites de taille, délais, fils de travail |
| Requête construite comme une chaîne | Injection SQL, y compris aveugle | Paramètres liés, liste fermée pour les identifiants |
| Compte applicatif trop doté | Lecture ou destruction au-delà du besoin | Moindre privilège, compte de migration séparé |
| Paquets npm exécutés avec tous les droits | Code tiers malveillant | Fichier de verrouillage, installation stricte, inventaire des dépendances |
Prérequis. Une version de Node.js encore maintenue ; un fichier de verrouillage versionné et une installation qui le respecte (npm ci) ; un pilote de base dont la préparation côté serveur est vérifiée ; deux comptes de base distincts (application, migrations) ; une bibliothèque de validation par schéma branchée sur chaque point d’entrée.
Ce que ces mécanismes ne garantissent pas. Une requête paramétrée empêche l’injection, pas la lecture d’une ligne qui appartient à quelqu’un d’autre : l’autorisation reste à écrire. Un ORM ne garantit ni les performances ni l’absence de requêtes brutes. TypeScript ne garantit rien à l’exécution. Une transaction ne garantit pas l’absence de course au niveau d’isolation par défaut. Et la base ne voit qu’un compte applicatif derrière le pool : l’attribution d’une action à un utilisateur vient des journaux de l’application, pas de ceux du moteur.
Exploitation. Le secret de connexion à la base vit hors du dépôt, avec une rotation prévue sans arrêt de service (voir la prévention des secrets dans le code source). À superviser : le retard de la boucle d’événements (Node.js l’expose dans perf_hooks), l’occupation du pool, les requêtes lentes, les erreurs de sérialisation et les refus de droits, ces derniers étant un excellent signal d’injection en cours. Un délai maximal d’exécution par requête (statement_timeout sous PostgreSQL) borne les dégâts d’une requête pathologique. Les migrations de schéma sont versionnées et rejouables, et une sauvegarde compte à partir du jour où sa restauration a été testée, pas avant. Les pages sœurs du groupe couvrent Python en automatisation défensive et Bash pour le scripting système.
Une procédure stockée protège-t-elle de l’injection SQL ?
Section intitulée « Une procédure stockée protège-t-elle de l’injection SQL ? »Seulement si elle n’assemble pas elle-même du SQL. Une procédure qui reçoit des paramètres et les utilise comme valeurs est sûre ; une procédure qui concatène ses paramètres dans une chaîne puis l’exécute dynamiquement déplace l’injection dans la base, avec les droits du propriétaire de la procédure, souvent plus larges que ceux de l’application.
Échapper les apostrophes à la main revient-il au même qu’un paramètre lié ?
Section intitulée « Échapper les apostrophes à la main revient-il au même qu’un paramètre lié ? »Non. L’échappement dépend du dialecte, de l’encodage de la connexion et du contexte : dans une comparaison numérique, il n’y a aucune apostrophe à fermer et la charge passe sans en contenir une seule. Le paramètre lié supprime la question, puisque la valeur n’est jamais analysée comme du texte de requête.
Une base documentaire sans SQL supprime-t-elle le problème ?
Section intitulée « Une base documentaire sans SQL supprime-t-elle le problème ? »Elle supprime la syntaxe, pas la confusion entre code et donnée. Quand un filtre est un objet et que le corps JSON de la requête fournit cet objet, l’utilisateur peut y glisser des opérateurs à la place d’une valeur. La parade est la même : vérifier le type réel de chaque champ avant qu’il n’atteigne la couche d’accès aux données.
🐝 Ce qui arrive si on s’en passe
Section intitulée « 🐝 Ce qui arrive si on s’en passe »Sans ces modèles en tête, une revue valide ce qu’elle voit et laisse passer ce qu’elle ne sait pas lire. Un contrôle écrit dans le navigateur est compté comme une protection, alors que le serveur accepte n’importe quelle requête forgée. Un type déclaré passe pour une vérification, et un objet inattendu atteint la couche d’accès aux données. Un rapport de scan devient intriable : faute de distinguer une requête assemblée d’une requête liée, tout est corrigé ou rien ne l’est. Côté base, un compte applicatif propriétaire du schéma transforme une seule requête fautive en accès complet, et l’absence de journal applicatif relié à l’utilisateur prive l’investigation de toute attribution. Enfin, une course entre deux requêtes produit des écritures incohérentes que personne ne sait expliquer.
🦫 Explication de la marmotte 🍫
Section intitulée « 🦫 Explication de la marmotte 🍫 »Un site web utilise deux langages. JavaScript est un langage de programmation qui s’exécute dans le navigateur du visiteur, et souvent aussi sur le serveur, l’ordinateur distant qui héberge le site. Dans le navigateur, il est enfermé : il modifie la page affichée, mais ne lit pas les fichiers de l’ordinateur. Sur le serveur, il dispose de tous les droits du programme : fichiers, réseau, lancement d’autres programmes. SQL est le langage qui interroge la base de données, là où le site range ses informations. On y décrit le résultat voulu, et la base choisit elle-même comment le trouver. Le visiteur saisit une information, le navigateur l’envoie au serveur, le serveur adresse une demande à la base, la base répond, puis le navigateur affiche la réponse. À chaque passage, celui qui reçoit doit tout revérifier : le navigateur appartient au visiteur, qui peut envoyer ce qu’il veut. Le serveur doit transmettre à la base le texte de la demande d’un côté et les informations saisies de l’autre. S’il mélange les deux dans une seule phrase, un visiteur malveillant y glisse ses propres ordres, et la base les exécute. Même danger au retour : une information enregistrée, affichée comme un morceau de page et non comme du texte, s’exécute chez les autres visiteurs. Enfin, le serveur se connecte à la base avec des droits réduits, pour qu’une erreur ne donne pas accès à tout.
À lire aussi
- Écrire un outil jetable proprement, sans faille ni detteComment écrire vite un outil jetable (script ponctuel d'extraction, de migration ou de purge) sans qu'il devienne une faille ou une dette, et à quels signes il a cessé d'être jetable.
- Non-interférence et contrôle de flux d'information en pratiqueCe que le contrôle de flux d'information et la non-interférence ajoutent au contrôle d'accès, leurs variantes, leurs limites et leur usage en architecture de sécurité.
- OWASP Top 10 ASVS ZAP et sécurité applicativePrésentation de l’OWASP, de son mandat communautaire, de ses référentiels de sécurité applicative et de leur exploitation en architecture.
- L'injection de prompt n'est pas un bugL'injection de prompt n'est pas un défaut qu'un correctif fera disparaître : elle découle de ce qu'un modèle de langage reçoit instructions et données dans le même canal. On ne la résout pas en filtrant ce que le modèle lit, on la contient en limitant ce qu'il peut faire.
Pages voisines
La citent
JavaScript et SQL côté web, ce qu'il faut savoir des deux langages
Elle cite
- Lien dans les deux sens :Langages informatiques : histoire, inventeurs et usages
- Lien dans les deux sens :Comment lire un rapport OWASP Top 10
- Écrire des scripts Bash système fiables et sûrs en production
- Python pour l'automatisation défensive, usages et garde-fous
- Attributs du cookie de session
- Comment revoir le code de sécurité au-delà du linter
- Maîtriser le risque des dépendances transitives (npm, pip, Maven, Go)
- Prévenir les secrets codés en dur dans Git
- Sécuriser les mises à jour automatiques de dépendances et semver
- Cross-site scripting (XSS) : mécanisme, variantes et défenses
- Démonstration de pollution de prototype JavaScript
: les deux pages se citent l’une l’autre.
