Les applications MongoDB sont exposées à des risques de sécurité importants lorsque les données saisies par les utilisateurs ne sont pas validées. Les attaques par injection nosql peuvent compromettre votre base de données de manière silencieuse, exposant ainsi des données sensibles et compromettant l'intégrité du système. Ce guide présente des stratégies défensives éprouvées pour assainir les entrées, sécuriser les requêtes de base de données et mettre en place une protection robuste contre les menaces basées sur l'injection.
Qu'est-ce qu'une injection nosql ?
Une injection nosql se produit lorsque des attaquants exploitent des entrées utilisateur insuffisamment validées pour manipuler la logique de requête d'une base de données. En injectant des charges utiles malveillantes, les attaquants peuvent contourner les mécanismes d'authentification, extraire des données non autorisées ou contourner les contrôles de sécurité des applications.
Contrairement à l'injection SQL, qui exploite les vulnérabilités dans la construction de requêtes basées sur des chaînes de caractères, l'injection nosql tire parti des structures de données flexibles et des requêtes dynamiques. Cela est particulièrement préoccupant lorsque les applications acceptent des entrées faiblement typées telles que interface{} ou map[string]interface{} sans validation appropriée.
Pourquoi MongoDB se distingue-t-il ?
Les requêtes MongoDB sont construites à l'aide de BSON (Binary JSON), des paires clé-valeur structurées, plutôt que des chaînes brutes. Cette conception élimine de nombreux risques d'injection traditionnels, car les requêtes ne sont pas assemblées par concaténation de chaînes.
Cependant, si les données saisies par l'utilisateur sont directement intégrées à ces objets BSON sans validation appropriée, les attaquants peuvent injecter des opérateurs MongoDB tels que $ne, $gtou $or pour modifier la logique de la requête.
Exemple : injection d'opérateur en Go
filter := bson.M{"name": inputName}
collection.Find(ctx, filter)
Si inputName est une chaîne simple telle que « Joe », il n'y a aucun problème. Cependant, si votre code autorisel' , map[string]interface{} ou JSON brut, un attaquant pourrait injecter des objets opérateurs tels que :
{ "$ne": null }
Cette charge utile modifie la logique de requête, en recherchant tous les documents où le nom apparaît plutôt qu'en filtrant une valeur spécifique, contournant ainsi efficacement les contrôles d'accès prévus par l'application.
Identifier les comportements à risque et les pièges courants
1. Confiance implicite dans les corps de requête
L'analyse directe des corps de requête en cartes BSON crée une voie d'injection permettant aux attaquants d'intégrer des opérateurs de requête malveillants et des constructions logiques complexes directement dans les opérations de base de données.
// Unsafe
var filter bson.M
_ = json.NewDecoder(req.Body).Decode(&filter)
Au lieu de cela, veuillez analyser uniquement les champs et les types connus :
type QueryInput struct {
Name string json:"name" validate:"required"
}
2. Accepter les types non spécifiés
Les vulnérabilités d'injection surviennent fréquemment lorsque les API acceptent des types de données trop permissifs tels que interface{} ou des cartes génériques. Ces structures flexibles peuvent héberger des opérateurs MongoDB intégrés ou des constructions d'objets malveillants qui contournent la logique de requête prévue.
Veuillez limiter l'utilisation de fixations lâches sauf en cas d'absolue nécessité et mettre en œuvre une validation rigoureuse lorsque cela est inévitable. Définissez plutôt des structures de données explicites qui correspondent au format d'entrée attendu et aux valeurs acceptables.
3. JSON dans les chaînes de requête ou les opérateurs de requête
Les API qui exposent des capacités de filtrage dynamique via des paramètres de requête URL ou des points de terminaison de recherche constituent des surfaces d'attaque intéressantes. Les attaquants ciblent fréquemment ces interfaces en intégrant des opérateurs codés en JSON et une logique conditionnelle directement dans les chaînes de requête. Cela transforme des paramètres de filtre légitimes en opérations malveillantes sur la base de données :
GET /search?filter={"price":{"$gt":0}}
Sans validation rigoureuse des entrées, cette approche permet aux attaquants de manipuler les structures de requête et de contourner les contrôles de sécurité, ce qui peut exposer des données non autorisées ou augmenter les privilèges au-delà des niveaux d'accès prévus.
Atténuation :
- Mettez en place des listes blanches de champs et d'opérateurs afin de limiter les paramètres de requête à des valeurs prédéfinies et sécurisées.
- Lors de la création de filtres de base de données, veuillez inclure explicitement toutes les valeurs fournies par l'utilisateur dans des opérateurs $eq afin d'empêcher l'injection d'opérateurs alternatifs.
4. Extension des requêtes logiques avec injection
Applications permettant à l'utilisateur de contrôler les opérateurs logiques tels que $or créent des opportunités pour des attaques par extension de requête. Les attaquants peuvent insérer des expressions conditionnelles supplémentaires dans ces opérateurs, modifiant ainsi fondamentalement la sémantique des requêtes et contournant potentiellement les contrôles d'authentification ou les restrictions d'accès aux données.
filter := bson.M{
"$or": []bson.M{
{"role": "user"},
{"active": true},
},
}
Si les données saisies par l'utilisateur modifient directement le clause $or , les attaquants peuvent injecter une condition non autorisée :
{ "$or": [ {"role": "user"}, {"active": true}, {"isAdmin": true} ] }
Cette requête modifiée pourrait contourner les contrôles d'accès basés sur les rôles, exposant potentiellement des données administratives ou accordant des privilèges élevés à des utilisateurs non autorisés. Empêchez la modification dynamique des opérateurs logiques en mettant en œuvre une validation stricte des entrées et en évitant que les utilisateurs contrôlent directement la structure des requêtes.
GET /search?filter={"price":{"$gt":0}}
Sans une validation rigoureuse des entrées, cette approche permet aux attaquants de manipuler les structures de requête et de contourner les contrôles de sécurité, exposant potentiellement des données non autorisées.
Atténuation :
- Mettre en place des listes d'autorisation pour les champs et opérateurs autorisés, en rejetant tout paramètre de requête ne respectant pas ces limites prédéfinies.
- Appliquez une correspondance explicite en encapsulant toutes les entrées utilisateur dans $eq afin d'empêcher la substitution d'opérateurs de comparaison ou logiques.
Fonctionnalités d'exécution JavaScript
MongoDB offre plusieurs fonctionnalités d'exécution JavaScript côté serveur qui présentent des risques potentiels pour la sécurité, notamment :
Ces fonctionnalités présentent un risque important pour la sécurité et doivent être évitées, sauf si des exigences commerciales critiques nécessitent leur utilisation. Afin d'éviter toute exploitation, veuillez désactiver les scripts dans la configuration MongoDB :
Pour mongod:
--noscripting
Ou via le fichier de configuration :
security:
javascriptEnabled: false
Appliquez la même configuration aux mongos dans un déploiement fragmenté. mongos dans un déploiement fragmenté.
Les opérateurs de requête natifs de MongoDB offrent des fonctionnalités complètes qui éliminent le besoin d'exécuter du JavaScript dans pratiquement tous les cas d'utilisation. Évitez complètement les clauses$where de l' , sauf si vous avez un contrôle absolu sur la validation des entrées et le contexte d'exécution.
Comment prévenir les injections nosql
1. Vérifier rigoureusement les données saisies
Mettez en œuvre une validation stricte des types qui n'accepte que les types de données primitifs attendus, tels que les chaînes de caractères, les entiers ou les booléens. Veuillez rejeter tout objet imbriqué, tableau ou nom de champ non reconnu susceptible de contenir des opérations intégrées ou des constructions malveillantes.
type LoginInput struct {
Username string json:"username" validate:"required,alphanum"
Password string json:"password" validate:"required,min=8"
}
2. Veuillez ne pas faire confiance à la structure des requêtes provenant des utilisateurs.
Construisez toutes les requêtes de base de données à l'aide de mappages de champs prédéfinis et contrôlés par l'application, ainsi que de modèles de requêtes. Empêcher les entrées utilisateur d'influencer l'architecture des requêtes, les opérateurs logiques ou les structures conditionnelles qui pourraient compromettre les modèles d'accès aux données prévus.
filter := bson.M{
"username": input.Username,
"password": input.Password,
}
Veuillez éviter :
// Dangerous: allows injection of operators
_ = json.NewDecoder(req.Body).Decode(&filter)
La plupart des pilotes MongoDB fournissent des générateurs de requêtes fortement typés qui imposent une construction structurée des requêtes et empêchent les vulnérabilités liées à des injections accidentelles. Veuillez exploiter ces interfaces natives plutôt que d'exposer des cartes de requêtes brutes ou des objets BSON via les points de terminaison de l'application.
filter := bson.M{
"username": input.Username,
"password": input.Password,
}
Veuillez éviter :
// Dangerous: allows injection of operators
_ = json.NewDecoder(req.Body).Decode(&filter)
3. Injection de blocs d'opérateurs MongoDB
Empêcher les entrées contrôlées par l'utilisateur de fonctionner comme des opérateurs de requête ou des constructions logiques. Veuillez traiter toutes les données utilisateur comme des valeurs littérales plutôt que comme des composants de requête exécutables.
Au lieu de :
filter := bson.M{
"status": input.Status,
}
Utilisation :
filter := bson.M{
"status": bson.M{"$eq": input.Status},
}
Cette approche impose une correspondance stricte entre les égalités et empêche l'exécution d'opérateurs injectés tels que $ne, $inou $regex ou d'autres modificateurs logiques susceptibles de contourner la logique de requête prévue.
4. Utiliser le principe du moindre privilège pour les utilisateurs de la base de données
Veuillez configurer les connexions à la base de données de l'application avec des comptes utilisateurs qui ne disposent que des autorisations minimales requises pour les opérations prévues. Veuillez éviter d'utiliser des identifiants de niveau administrateur dans les configurations d'application afin de prévenir tout dommage potentiel résultant d'attaques par injection réussies.
5. Surveiller et tester
Mettez en place une journalisation complète afin de détecter les modèles de requêtes inhabituels et établissez des protocoles de tests de sécurité réguliers. Vérifiez la sécurité des terminaux en tentant des attaques par injection à l'aide de charges utiles malformées telles que :
{ "$ne": null }
Veuillez vérifier que l'application le rejette.
Cas de test à envisager :
- Objets au lieu de chaînes : { « username » : { « $ne » : null } }
- Tableaux au lieu de scalaires : { « rôles » : [ { « $gt » : « » } ] }
- Opérateurs en tant que valeurs de requête : { « field » : { « $regex » : « .* » } }
Liste de contrôle récapitulative
- s de validation des entrées: Appliquez des types de données stricts, validez les formats et rejetez les champs inattendus.
- s de construction de requêtes: Créez toutes les requêtes à l'aide de schémas prédéfinis et de modèles contrôlés par l'application.
- s de protection de l'opérateur: Bloquez les opérateurs MongoDB dans les entrées utilisateur en encapsulant les valeurs dans $eq ou en utilisant des API typées.
- JavaScript côté serveur: Veuillez supprimer les clauses $where et désactiver l'exécution JavaScript dans les configurations de production.
- s relatives au contrôle d'accès à la base de données: Configurez les connexions aux applications avec les autorisations minimales requises, en évitant les identifiants administratifs.
- s de test et de surveillance: Effectuez régulièrement des tests d'injection et mettez en place un système d'enregistrement des requêtes anormales.
Conclusions finales
Les vulnérabilités liées aux injections nosql peuvent être entièrement évitées grâce à des pratiques de codage sécurisées rigoureuses. La mise en œuvre d'une validation rigoureuse des entrées, d'une construction structurée des requêtes et de contrôles de sécurité approfondis permet aux développeurs de préserver la flexibilité et les performances des applications tout en éliminant les vecteurs d'attaque par injection.
Pour réussir, il est nécessaire de bien comprendre le flux de données complet de votre application, depuis l'analyse de la requête initiale jusqu'à l'exécution finale de la requête. Adoptez une approche « zero trust » pour toutes les sources d'entrée, mettez en œuvre une validation aux limites du système et privilégiez les principes de programmation défensive dans l'ensemble de votre infrastructure technologique.
Questions fréquentes
Pourquoi l'injection nosql représente-t-elle un risque si MongoDB utilise BSON à la place des chaînes de caractères ?
L'injection peut toujours se produire avant la création de l'objet BSON. Si les données saisies par l'utilisateur sont analysées dans des types flexibles tels que map[string]interface{} ou interface{} sans validation, les attaquants peuvent injecter des opérateurs MongoDB tels que $ne ou $or et manipuler la logique de requête.
Est-il sécurisé de décoder directement le corps d'une requête en bson.M ou map[string]interface{} ?
Non. Cela permet aux utilisateurs d'injecter des opérateurs de requête arbitraires. Veuillez toujours mapper les données de requête à des types stricts (par exemple, les structures Go) et valider les entrées avant de créer des requêtes.
Quel est le problème d'utiliser $where si l'entrée est validée ?
Même si l'entrée est partiellement validée, $where exécute JavaScript sur le serveur. Toute erreur dans l'échappement ou la validation peut entraîner une injection de code. Il est plus prudent d'éviter complètement $where ou de désactiver les scripts dans votre configuration MongoDB.
Comment puis-je vérifier si mon point de terminaison est vulnérable à une injection nosql ?
Veuillez essayer d'injecter des charges utiles telles que :
{ "username": { "$ne": null } }
Si le point de terminaison renvoie une réponse valide ou contourne la logique (par exemple, la connexion), l'entrée n'est pas correctement validée ou restreinte.
Quelle est la meilleure méthode pour créer des requêtes en toute sécurité dans Go ?
Veuillez utiliser des structures typées pour la saisie, éviter le décodage dans des cartes génériques et encapsuler les valeurs dans $eq si nécessaire. Il est important de ne jamais permettre à l'utilisateur de définir des opérateurs ou d'insérer directement des parties de la requête.