Empêcher l'injection SQL dans Node.js

86

Est-il possible d'empêcher les injections SQL dans Node.js (de préférence avec un module) de la même manière que PHP avait préparé des instructions qui les protégeaient.

Si c'est le cas, comment? Sinon, quels sont quelques exemples qui pourraient contourner le code que j'ai fourni (voir ci-dessous).


Un certain contexte:

Je crée une application Web avec une pile back-end composée de Node.js + MySql en utilisant le module node-mysql . Du point de vue de la convivialité, le module est génial, mais il n'a pas encore implémenté quelque chose qui ressemble aux instructions préparées de PHP (bien que je sache que c'est sur le todo ).

D'après ce que je comprends, l'implémentation par PHP d'instructions préparées, entre autres, a grandement contribué à la prévention des injections SQL. Je crains cependant que mon application node.js ne soit ouverte à des attaques similaires, même avec la chaîne d'échappement fournie par défaut (comme dans l'extrait de code ci-dessous).

node-mysql semble être le connecteur mysql le plus populaire pour node.js, donc je me demandais ce que d'autres personnes pourraient faire (le cas échéant) pour expliquer ce problème - ou si c'est même un problème avec node.js pour commencer (je ne sais pas comment cela ne serait pas, car l'entrée côté utilisateur / client est impliquée).

Dois-je passer à node-mysql-native pour le moment, car il fournit des instructions préparées? J'hésite à le faire, car il ne semble pas être aussi actif que node-mysql (bien que cela puisse simplement signifier qu'il est complet).

Voici un extrait de code d'enregistrement de l'utilisateur, qui utilise le module sanitizer , ainsi que la syntaxe de type instruction préparée de node-mysql (qui, comme je l'ai mentionné ci-dessus, échappe les caractères), pour empêcher respectivement les scripts intersites et les injections sql:

// Prevent xss
var clean_user = sanitizer.sanitize(username);

// assume password is hashed already
var post = {Username: clean_user, Password: hash};

// This just uses connection.escape() underneath
var query = connection.query('INSERT INTO users SET ?', post,
   function(err, results)
   {
       // Can a Sql injection happen here?
   });
funseiki
la source

Réponses:

59

La node-mysqlbibliothèque effectue automatiquement un échappement lorsqu'elle est utilisée comme vous le faites déjà. Voir https://github.com/felixge/node-mysql#escaping-query-values

Michael Pratt
la source
3
Comme mentionné dans mon article, je suis conscient que la bibliothèque échappe les caractères, mais je suis plus préoccupé par les implications de sécurité si je ne passe pas à une bibliothèque qui a implémenté des instructions préparées, c'est-à-dire y a-t-il une injection SQL qui peut se produire avec quoi Je fais actuellement?
funseiki
2
L'échappement des caractères empêche l'injection SQL. Les injections se produisent lorsque les caractères ne sont pas échappés, et les utilisateurs malveillants peuvent exploiter cela pour fermer la requête et en créer une nouvelle pour, par exemple, supprimer une table ou insérer un faux enregistrement. Avec les caractères échappés, ce n'est pas possible. Wikipedia a quelques informations supplémentaires sur l'injection SQL.
Michael Pratt
4
Mais cela empêche-t-il toutes les injections SQL? Cette réponse suggère que non (du moins pour PHP + MySQL) et implique que les instructions préparées de PHP le font. Encore une fois, c'est dans le contexte de PHP.
funseiki
1
Selon votre lien, cela ne fonctionne que sur les versions obsolètes de MySQL. Je ne sais pas si cette attaque particulière fonctionne sur Node, mais il semble que cela ait à voir avec des vulnérabilités PHP très spécifiques, donc mon instinct est non. Je ne dis pas qu'il n'y a absolument aucune vulnérabilité dans node-mysql, mais il est déjà utilisé dans de nombreux environnements de production. Si vous êtes toujours préoccupé par l'injection SQL, je vous suggère de mordre la balle et d'essayer quelque chose comme MongoDB - ne peut pas faire une injection SQL si vous n'utilisez pas SQL.
Michael Pratt
1
Cela ressemblait à cela et la route MongoDB est un bon point - bien que la conception actuelle se prêterait bien à un schéma relationnel. J'attendrai de voir si quelqu'un d'autre a un aperçu des vulnérabilités de sécurité - sinon, il semble que le consensus soit simplement de s'en tenir à node-mysql
funseiki
12

La bibliothèque a une section dans le readme sur l'échappement. C'est Javascript natif, donc je ne suggère pas de passer à node-mysql-native . La documentation indique ces instructions pour s'échapper:

Edit: node-mysql-native est aussi une solution purement Javascript.

  • Les nombres ne sont pas modifiés
  • Les booléens sont convertis en true/ falsestrings
  • Les objets de date sont convertis en YYYY-mm-dd HH:ii:sschaînes
  • Les tampons sont convertis en chaînes hexadécimales, par exemple X'0fa5'
  • Les chaînes sont échappées en toute sécurité
  • Les tableaux sont transformés en liste, par exemple ['a', 'b']en'a', 'b'
  • Les tableaux imbriqués sont transformés en listes groupées (pour les insertions groupées), par exemple [['a', 'b'], ['c', 'd']]se transforment en('a', 'b'), ('c', 'd')
  • Les objets sont transformés en key = 'val'paires. Les objets imbriqués sont convertis en chaînes.
  • undefined/ nullsont convertis enNULL
  • NaN/ Infinitysont laissés tels quels. MySQL ne les prend pas en charge et essayer de les insérer en tant que valeurs déclenchera des erreurs MySQL jusqu'à ce qu'elles implémentent le support.

Cela vous permet de faire des choses comme ceci:

var userId = 5;
var query = connection.query('SELECT * FROM users WHERE id = ?', [userId], function(err, results) {
  //query.sql returns SELECT * FROM users WHERE id = '5'
});

Ainsi que cette:

var post  = {id: 1, title: 'Hello MySQL'};
var query = connection.query('INSERT INTO posts SET ?', post, function(err, result) {
  //query.sql returns INSERT INTO posts SET `id` = 1, `title` = 'Hello MySQL'
});

En plus de ces fonctions, vous pouvez également utiliser les fonctions d'échappement:

connection.escape(query);
mysql.escape(query);

Pour échapper les identificateurs de requête:

mysql.escapeId(identifier);

Et en réponse à votre commentaire sur les déclarations préparées:

Du point de vue de la convivialité, le module est excellent, mais il n'a pas encore implémenté quelque chose qui ressemble aux instructions préparées de PHP.

Les instructions préparées sont sur la liste des tâches pour ce connecteur, mais ce module vous permet au moins de spécifier des formats personnalisés qui peuvent être très similaires aux instructions préparées. Voici un exemple du readme:

connection.config.queryFormat = function (query, values) {
  if (!values) return query;
  return query.replace(/\:(\w+)/g, function (txt, key) {
    if (values.hasOwnProperty(key)) {
      return this.escape(values[key]);
    }
    return txt;
  }.bind(this));
};

Cela modifie le format de requête de la connexion afin que vous puissiez utiliser des requêtes comme celle-ci:

connection.query("UPDATE posts SET title = :title", { title: "Hello MySQL" });
//equivalent to
connection.query("UPDATE posts SET title = " + mysql.escape("Hello MySQL");
hexacyanide
la source
Merci pour la réponse - je suis conscient du style préparé. En dessous, cependant, les personnages sont échappés. Voir: "Cependant, il utilise simplement le même connection.escape ()" . Pour ce qui est de ne pas utiliser node-mysql-native: c'est ce avec quoi je me bats. Si node-mysql-native implémente des instructions préparées et que ses implémentations empêchent les injections SQL, ne devrais-je pas faire le changement tant que node-mysql ne les a pas?
funseiki
C'est une sorte de question sur la poule et l'œuf. Je ne développe pas activement mon pilote parce que la plupart des gens utilisent @ felixge. J'essaierai probablement de trouver un peu de temps pour porter des instructions préparées sur node-mysql car cela donne en effet des avantages en termes de performances (et rend potentiellement les injections sql plus difficiles). N'hésitez pas à commenter / publier des problèmes si vous décidez de l'
essayer
1
@funseiki Je suis sûr que les instructions préparées seraient la meilleure solution, mais je suis très sûr que l'échappement empêchera les injections SQL. Puisque le module lui-même est pris en charge par Joyent, le module est actif et évidemment soigneusement vérifié. Si ce module n'était pas prêt pour la production, je ne pense pas que le module aurait en moyenne 1000 téléchargements / jour le mois dernier. Notez que node-mysql-native est de 6 mois depuis son dernier développement, et node-mysql est très actif, avec plusieurs personnes travaillant dessus.
hexacyanide
@AndreySidorov Merci pour le commentaire - si je tente de m'y attaquer, je publierai une mise à jour. Je ne pense pas que ce sera de si tôt, car il ne semble pas que ce soit une bête facile à gérer (cela nécessitera plus de recherches que je n'en ai actuellement le temps). Merci également d'avoir
créé
@hexacyanide Parce que node-mysql est si populaire, j'espérais pouvoir obtenir une réponse des membres de la communauté concernant les problèmes de sécurité qu'ils auraient pu rencontrer (ou évités) ainsi qu'un argument convaincant expliquant pourquoi l'approche actuelle d'échappement de personnage est sécurisée assez pour leur code.
funseiki
12

Je me rends compte que c'est un article plus ancien, mais il semble qu'une réponse n'a jamais été marquée, alors je vais la jeter là-bas.

En ce qui concerne le test si un module que vous utilisez est sécurisé ou non, vous pouvez emprunter plusieurs itinéraires. Je vais aborder les avantages / inconvénients de chacun afin que vous puissiez prendre une décision plus éclairée.

Actuellement, il n'y a aucune vulnérabilité pour le module que vous utilisez, cependant, cela peut souvent conduire à un faux sentiment de sécurité car il pourrait très bien y avoir une vulnérabilité exploitant actuellement le module / progiciel que vous utilisez et vous ne seriez pas alerté à un problème jusqu'à ce que le fournisseur applique un correctif / correctif.

  1. Pour vous tenir au courant des vulnérabilités, vous devrez suivre les listes de diffusion, les forums, les IRC et autres discussions liées au piratage. PRO: Vous pouvez souvent prendre conscience de problèmes potentiels au sein d'une bibliothèque avant qu'un fournisseur n'ait été alerté ou n'ait publié un correctif / correctif pour remédier à la voie potentielle d'attaque sur son logiciel. CONTRE: Cela peut prendre beaucoup de temps et de ressources. Si vous suivez cette route, un bot utilisant des flux RSS, une analyse des journaux (journaux de discussion IRC) et / ou un scrapper Web utilisant des phrases clés (dans ce cas, node-mysql-native) et des notifications peuvent aider à réduire le temps passé à troller ces ressources.

  2. Créez un fuzzer, utilisez un fuzzer ou un autre framework de vulnérabilité tel que metasploit , sqlMap etc. pour aider à tester les problèmes que le fournisseur n'a peut-être pas recherchés. PRO: Cela peut s'avérer être une méthode sûre pour garantir à un niveau acceptable si le module / logiciel que vous implémentez est sûr pour l'accès public. INCONVÉNIENTS: Cela prend également du temps et est coûteux. L'autre problème proviendra de faux positifs ainsi que d'un examen sans instruction des résultats là où un problème réside mais n'est pas remarqué.

La sécurité réelle et la sécurité des applications en général peuvent être très longues et gourmandes en ressources. Une chose que les gestionnaires utiliseront toujours est une formule pour déterminer la rentabilité (main-d'œuvre, ressources, temps, rémunération, etc.) de l'exécution des deux options ci-dessus.

Quoi qu'il en soit, je me rends compte que ce n'est pas une réponse «oui» ou «non» qui aurait pu être espérée, mais je ne pense pas que quiconque puisse vous la donner jusqu'à ce qu'il effectue une analyse du logiciel en question.

jas-
la source
3

Je sais que cette question est ancienne mais pour toute personne intéressée, Mysql-native est obsolète, il est donc devenu MySQL2 qui est un nouveau module créé avec l'aide de l'équipe du module MySQL d'origine. Ce module a plus de fonctionnalités et je pense qu'il a ce que vous voulez car il a préparé des déclarations (en utilisant.execute ()) comme en PHP pour plus de sécurité.

C'est aussi très actif (le dernier changement était de 2-1 jours) Je ne l'ai pas essayé avant mais je pense que c'est ce que vous voulez et plus.

Garçon pro
la source
-1

Le moyen le plus simple est de gérer toutes les interactions de votre base de données dans son propre module que vous exportez vers vos itinéraires. Si votre route n'a pas de contexte de la base de données, SQL ne peut pas y toucher de toute façon.

Papa oiseau
la source
Cela ne répond pas vraiment à la question de OP sur la désinfection.
Christopher