La sécurité de MongoDB n'est pas entièrement garantie en configurant simplement des certificats d'authentification ou en chiffrant les données. Certains attaquants « feront un effort supplémentaire » en jouant avec les paramètres reçus dans les requêtes HTTP qui sont utilisées dans le cadre du processus de requête de la base de données.
Les bases de données SQL sont les plus vulnérables à ce type d'attaque, mais l'injection externe est également possible dans les DBM NoSQL comme MongoDB. Dans la plupart des cas, les injections externes se produisent à la suite d'une concaténation non sécurisée de chaînes lors de la création de requêtes.
Qu'est-ce qu'une attaque par injection externe ?
L'injection de code consiste essentiellement à intégrer des données non validées (vecteur non atténué) dans un programme vulnérable qui, une fois exécuté, conduit à un accès désastreux à votre base de données ; menaçant sa sécurité.
Lorsque des variables non épurées sont passées dans une requête MongoDB, elles cassent la structure d'orientation de la requête du document et sont parfois exécutées comme le code javascript lui-même. C'est souvent le cas lors de la transmission d'accessoires directement depuis le module body-parser pour le serveur Nodejs. Par conséquent, un attaquant peut facilement insérer un objet Js là où vous attendez une chaîne ou un nombre, obtenant ainsi des résultats indésirables ou en manipulant vos données.
Considérez les données ci-dessous dans la collection d'un étudiant.
{username:'John Doc', email:'[email protected]', age:20},
{username:'Rafael Silver', email:'[email protected]', age:30},
{username:'Kevin Smith', email:'[email protected]', age:22},
{username:'Pauline Wagu', email:'[email protected]', age:23}
Supposons que votre programme doit récupérer tous les étudiants dont l'âge est égal à 20 ans, vous écririez un code comme celui-ci...
app.get(‘/:age’, function(req, res){
db.collections(“students”).find({age: req.params.age});
})
Vous aurez soumis un objet JSON dans votre requête http en tant que
{age: 20}
Ceci renverra tous les étudiants dont l'âge est égal à 20 comme résultat attendu et dans ce cas uniquement {nom d'utilisateur :'John Doc', email :'[email protected]', âge :20} .
Maintenant, disons qu'un attaquant soumet un objet au lieu d'un nombre, c'est-à-dire {'$gt:0'} ;
La requête résultante sera :
db.collections(“étudiants”).find({age :{‘$gt:0’}); qui est une requête valide qui, une fois exécutée, renverra tous les élèves de cette collection. L'attaquant a une chance d'agir sur vos données en fonction de ses intentions malveillantes. Dans la plupart des cas, un attaquant injecte un objet personnalisé contenant des commandes MongoDB qui lui permettent d'accéder à vos documents sans la procédure appropriée.
Certaines commandes MongoDB exécutent du code Javascript dans le moteur de base de données, un risque potentiel pour vos données. Certaines de ces commandes sont ‘$where’, ‘$group’ et ‘mapReduce’. Pour les versions antérieures à MongoDB 2.4, le code Js a accès à l'objet db depuis la requête.
Protections natives MongoDB
MongoDB utilise les données BSON (Binary JSON) pour ses requêtes et ses documents, mais dans certains cas, il peut accepter des expressions JSON et Js non sérialisées (telles que celles mentionnées ci-dessus). La plupart des données transmises au serveur sont au format d'une chaîne et peuvent être introduites directement dans une requête MongoDB. MongoDB n'analyse pas ses données, évitant ainsi les risques potentiels pouvant résulter de l'intégration de paramètres directs.
Si une API implique l'encodage de données dans un texte formaté et que ce texte doit être analysé, cela peut créer un désaccord entre l'appelant du serveur et l'appelé de la base de données sur la manière dont cette chaîne va être analysée . Si les données sont accidentellement interprétées à tort comme des métadonnées, le scénario peut potentiellement poser des menaces de sécurité pour vos données.
Exemples d'injections externes MongoDB et comment les gérer
Considérons les données ci-dessous dans une collection d'étudiants.
{username:'John Doc', password: ‘16djfhg’, email:'[email protected]', age:20},
{username:'Rafael Silver',password: ‘djh’, email:'[email protected]', age:30},
{username:'Kevin Smith', password: ‘16dj’, email:'[email protected]', age:22},
{username:'Pauline Wagu', password: ‘g6yj’, email:'[email protected]', age:23}
Injection à l'aide de l'opérateur $ne (pas égal)
Si je veux retourner le document avec le nom d'utilisateur et le mot de passe fournis à partir d'une demande, le code sera :
app.post('/students, function (req, res) {
var query = {
username: req.body.username,
password: req.body.password
}
db.collection(students).findOne(query, function (err, student) {
res(student);
});
});
Si nous recevons la demande ci-dessous
POST https://localhost/students HTTP/1.1
Content-Type: application/json
{
"username": {"$ne": null},
"password": {"$ne": null}
}
La requête renverra définitivement le premier étudiant dans ce cas puisque son nom d'utilisateur et son mot de passe ne sont pas évalués comme étant nuls. Ce n'est pas conforme aux résultats attendus.
Pour résoudre ce problème, vous pouvez utiliser :
module mongo-sanitize qui empêche toute clé commençant par "$" d'être transmise au moteur de requête MongoDB.
Installez d'abord le module
npm install mongo-sanitize
var sanitize = require(‘mongo-sanitize’);
var query = {
username: req.body.username,
password: req.body.password
}
Utiliser mangouste pour valider les champs de votre schéma de sorte que s'il attend une chaîne et reçoit un objet, la requête génère une erreur. Dans notre cas ci-dessus, la valeur nulle sera convertie en une chaîne "" qui n'a littéralement aucun impact.
Injection à l'aide de l'opérateur $where
C'est l'un des opérateurs les plus dangereux. Cela permettra à une chaîne d'être évaluée à l'intérieur du serveur lui-même. Par exemple, pour récupérer les étudiants dont l'âge est supérieur à une valeur Y, la requête sera
var query = {
$where: “this.age > ”+req.body.age
}
db.collection(students).findOne(query, function (err, student) {
res(student);
});
L'utilisation du module de désinfection n'aidera pas dans ce cas si nous avons un '0 ; return true' car le résultat renverra tous les étudiants plutôt que ceux dont l'âge est supérieur à une valeur donnée. Les autres chaînes possibles que vous pouvez recevoir sont ‘\’ ; return \ '\' ==\'' or this.email ==='';return '' ==''. Cette requête renverra tous les étudiants plutôt que seulement ceux qui correspondent à la clause.
La clause $where doit être grandement évitée. Outre le revers décrit, cela réduit également les performances car il n'est pas optimisé pour utiliser des index.
Il y a aussi une grande possibilité de passer une fonction dans la clause $where et la variable ne sera pas accessible dans la portée de MongoDB, ce qui peut entraîner le plantage de votre application. C'est-à-dire
var query = {
$where: function() {
return this.age > setValue //setValue is not defined
}
}
Vous pouvez également utiliser les opérateurs $eq, $lt, $lte, $gt, $gte à la place.
Se protéger de l'injection externe MongoDB
Voici trois choses que vous pouvez faire pour vous protéger...
- Valider les données utilisateur. Si l'on considère comment l'expression $where peut être utilisée pour accéder à vos données, il est conseillé de toujours valider ce que les utilisateurs envoient à votre serveur.
- Utilisez le concept de validateur JSON pour valider votre schéma avec le module mongoose.
- Concevez vos requêtes de sorte que le code Js n'ait pas un accès complet au code de votre base de données.
Conclusion
L'injection externe est également possible avec MongoDB. Il est souvent associé à des données utilisateur non validées entrant dans des requêtes MongoDB. Il est toujours important de détecter et d'empêcher l'injection NoSQL en testant toutes les données pouvant être reçues par votre serveur. S'il est négligé, cela peut menacer la sécurité des données des utilisateurs. La procédure la plus importante consiste à valider vos données à toutes les couches concernées.