FlawOpen

flawopen.com/SQL Injection/ID numériques

Pourquoi le formatage de chaîne est-il dangereux, même pour les ID numériques ?

Page de référence
Réponse courte

Le risque ne dépend pas du sens réel de la valeur : la requête est construite en collant du texte. « C’est toujours un nombre » est une hypothèse sur les données, pas une garantie appliquée par le code.

Quand l’hypothèse échoue

Les paramètres HTTP arrivent toujours sous forme de chaînes

Un paramètre d’URL ou de formulaire est transmis comme texte, quel que soit le type attendu par la colonne en base. Sans validation explicite, le framework n’empêche pas l’envoi de id=1 OR 1=1 à la place d’un nombre avant que le code le reçoive.

Une refactorisation peut changer discrètement l’origine de la valeur

Un code sûr aujourd’hui parce que l’ID vient toujours d’une source interne fiable peut devenir vulnérable si un nouvel appelant transmet une valeur utilisateur à la même fonction. La faille était latente, en attente de ce changement.

La coercition de type peut masquer la valeur réelle

Dans les langages à typage dynamique notamment, une valeur qui semble numérique dans les journaux peut rester une chaîne contenant des caractères supplémentaires. Sans validation explicite, le type attendu ne garantit pas le type reçu.

Paramétrez la requête, même après validation du type

Validez l’ID selon le format attendu par l’application, puis transmettez-le via l’API de paramètres du pilote de base de données. La validation protège le contrat d’entrée ; la paramétrisation empêche l’interprétation de l’entrée comme syntaxe SQL. Le comportement du pilote sur le protocole peut varier : ne supposez pas que tous transmettent les valeurs de la même manière. Les paramètres PostgreSQL ne représentent pas les identifiants de table ou de colonne ; choisissez la structure dynamique de la requête dans une liste fixe autorisée par l’application.

Lier la valeur numérique et valider sa signification

Validez l’ID comme entier positif sûr si c’est le type attendu par l’application, mais liez-le tout de même comme valeur de requête. La validation protège le contrat d’entrée de l’application ; la paramétrisation empêche l’entrée de devenir de la syntaxe SQL. Les paramètres PostgreSQL ne remplacent pas les identifiants de table ou de colonne : choisissez toute structure de requête dynamique dans une liste fixe autorisée par l’application.

NON SÛR — interpolation de l’ID dans SQL
const rawId = new URL(request.url).searchParams.get("id");
const result = await pool.query(
  `SELECT id FROM users WHERE id = ${rawId}`
);
SÛR — valider l’ID et lier la valeur
const rawId = new URL(request.url).searchParams.get("id");
if (rawId === null || !/^[0-9]+$/.test(rawId)) {
  return new Response("Invalid id", { status: 400 });
}
const id = Number(rawId);
if (!Number.isSafeInteger(id) || id < 1) {
  return new Response("Invalid id", { status: 400 });
}
const result = await pool.query(
  "SELECT id FROM users WHERE id = $1",
  Array.of(id)
);

Références

À voir aussi Injection SQL (CWE-89) : fonctionnement et prévention dans chaque langage — flawopen.com Qu'est-ce qu'une requête paramétrée ? Prisma $queryRaw protège-t-il contre les injections SQL ? L’interpolation dans where() de Rails est-elle sûre ? Sequelize query() protège-t-il contre les injections SQL ? SQLAlchemy text() protège-t-il contre les injections SQL ? La vérification des requêtes sqlx à la compilation empêche-t-elle les injections SQL ?