Le applicazioni MongoDB affrontano seri rischi di sicurezza quando l’input dell’utente non viene validato. Gli attacchi di NoSQL injection possono violare silenziosamente il tuo database, esponendo dati sensibili e compromettendo l’integrità del sistema. Questa guida illustra strategie difensive collaudate per sanificare gli input, mettere in sicurezza le query al database e costruire una protezione robusta contro le minacce basate sull’injection.
Cos’è la NoSQL Injection?
La NoSQL injection avviene quando gli aggressori sfruttano input utente insufficientemente validati per manipolare la logica delle query al database. Iniettando payload malevoli, possono aggirare i meccanismi di autenticazione, estrarre dati non autorizzati o eludere i controlli di sicurezza dell’applicazione.
A differenza della SQL injection, che sfrutta vulnerabilità nella costruzione di query basate su stringhe, la NoSQL injection approfitta di strutture dati flessibili e query dinamiche. Questo è particolarmente pericoloso quando le applicazioni accettano input debolmente tipizzati come interface{} o map[string]interface{} senza una corretta validazione.
Perché MongoDB è diverso
Le query in MongoDB sono costruite usando oggetti BSON (Binary JSON)—coppie chiave-valore strutturate—invece di stringhe grezze. Questo design evita molti rischi tradizionali di injection perché le query non sono assemblate tramite concatenazione di stringhe.
Tuttavia, se l’input utente viene inserito direttamente in questi oggetti BSON senza la dovuta validazione, gli aggressori possono iniettare operatori MongoDB come $ne, $gt o $or per modificare la logica della query.
Esempio: operator injection in Go
filter := bson.M{"name": inputName}
collection.Find(ctx, filter)
Se inputName è una semplice stringa come "Joe", è sicuro. Ma se il tuo codice consente map[string]interface{} o JSON grezzo, un aggressore potrebbe iniettare oggetti operatore come:
{ "$ne": null }
Questo payload trasforma la logica della query, facendo corrispondere qualsiasi documento in cui il nome esiste invece di filtrare per un valore specifico—di fatto aggirando i controlli di accesso previsti dall’applicazione.
Riconoscere pattern non sicuri ed errori comuni
1. Fiducia implicita nei body delle richieste
Il parsing diretto dei body delle richieste in mappe BSON crea un percorso di injection in cui gli aggressori possono incorporare operatori di query malevoli e costrutti logici complessi direttamente nelle operazioni sul database.
// Unsafe
var filter bson.M
_ = json.NewDecoder(req.Body).Decode(&filter)
Meglio analizzare solo i campi e i tipi noti:
type QueryInput struct {
Name string json:"name" validate:"required"
}
2. Accettare tipi laschi
Le vulnerabilità di injection sorgono spesso quando le API accettano tipi di dato troppo permissivi come interface{} o mappe generiche. Queste strutture flessibili possono nascondere operatori MongoDB incorporati o costrutti oggetto malevoli che aggirano la logica di query prevista.
Riduci al minimo l’uso di tipizzazione lasca se non strettamente necessario e implementa una validazione rigorosa quando inevitabile. Definisci invece strutture dati esplicite che corrispondano al formato di input atteso e che vincolino i valori accettabili.
3. JSON nelle query string o come operatori di query
Le API che espongono funzionalità di filtro dinamico tramite parametri di query URL o endpoint di ricerca rappresentano superfici di attacco allettanti. Gli aggressori prendono spesso di mira queste interfacce incorporando operatori codificati in JSON e logica condizionale direttamente nelle query string. Ciò trasforma parametri di filtro legittimi in operazioni malevole sul database:
GET /search?filter={"price":{"$gt":0}}
Senza una convalida rigorosa degli input, questo approccio consente agli aggressori di manipolare le strutture di query e aggirare i controlli di sicurezza, potenzialmente esponendo dati non autorizzati o incrementando i privilegi oltre i livelli previsti di accesso.
Mitigazione:
- Implementa allowlist di campi e operatori per limitare i parametri di query a valori predefiniti e sicuri.
- Racchiudi esplicitamente tutti i valori forniti dall’utente in operatori $eq quando costruisci i filtri del database per prevenire l’iniezione di operatori alternativi.
4. Espansione delle query logiche tramite injection
Le applicazioni che consentono all’utente di controllare operatori logici come $or creano opportunità per attacchi di espansione delle query. Gli aggressori possono iniettare espressioni condizionali aggiuntive in questi operatori, alterando radicalmente la semantica della query e potenzialmente aggirando i controlli di autenticazione o le restrizioni di accesso ai dati.
filter := bson.M{
"$or": []bson.M{
{"role": "user"},
{"active": true},
},
}
Se l’input utente manipola direttamente la clausola $or, gli aggressori possono iniettare condizioni non autorizzate:
{ "$or": [ {"role": "user"}, {"active": true}, {"isAdmin": true} ] }
Questa query modificata potrebbe aggirare i controlli di accesso basati sui ruoli, esponendo potenzialmente dati amministrativi o concedendo privilegi elevati a utenti non autorizzati. Evita la modifica dinamica degli operatori logici implementando una validazione rigorosa degli input ed evitando che l’utente controlli direttamente la struttura della query.
GET /search?filter={"price":{"$gt":0}}
Senza una convalida rigorosa degli input, questo approccio consente agli aggressori di manipolare le strutture di query e aggirare i controlli di sicurezza, potenzialmente esponendo dati non autorizzati.
Mitigazione:
- Implementa allowlist per campi e operatori consentiti, rifiutando qualsiasi parametro di query al di fuori di questi confini predefiniti.
- Imponi il confronto per uguaglianza esplicita racchiudendo tutto l’input utente in operatori $eq per impedire la sostituzione di operatori di confronto o logici.
Funzionalità di esecuzione JavaScript
MongoDB offre diverse capacità di esecuzione JavaScript lato server che introducono potenziali rischi di sicurezza, tra cui:
Queste funzionalità creano una significativa superficie di esposizione e andrebbero evitate a meno che requisiti di business critici non ne impongano l’uso. Per prevenire lo sfruttamento, disabilita lo scripting nella configurazione di MongoDB:
Per mongod:
--noscripting
Oppure via file di configurazione:
security:
javascriptEnabled: false
Applica la stessa configurazione a mongos in un deployment sharded.
Gli operatori di query nativi di MongoDB offrono funzionalità complete che eliminano la necessità di esecuzione JavaScript nella quasi totalità dei casi d’uso. Evita completamente le clausole $where a meno di non mantenere un controllo assoluto sulla validazione dell’input e sul contesto di esecuzione.
Come prevenire la NoSQL Injection
1. Valida rigorosamente gli input
Implementa una validazione stretta dei tipi che accetti solo i tipi di dato primitivi attesi come stringhe, interi o booleani. Rifiuta qualsiasi oggetto annidato, array o nome di campo non riconosciuto che potrebbe contenere operazioni incorporate o costrutti malevoli.
type LoginInput struct {
Username string json:"username" validate:"required,alphanum"
Password string json:"password" validate:"required,min=8"
}
2. Non fidarti della struttura di query fornita dagli utenti
Costruisci tutte le query al database usando mappature di campi predefinite e template di query controllati dall’applicazione. Impedisci che l’input dell’utente influenzi l’architettura della query, gli operatori logici o le strutture condizionali che potrebbero sovvertire i pattern di accesso ai dati previsti.
filter := bson.M{
"username": input.Username,
"password": input.Password,
}
Evita:
// Dangerous: allows injection of operators
_ = json.NewDecoder(req.Body).Decode(&filter)
La maggior parte dei driver MongoDB fornisce builder di query fortemente tipizzati che impongono una costruzione strutturata e prevengono vulnerabilità accidentali di injection. Sfrutta queste interfacce native invece di esporre mappe di query grezze o oggetti BSON tramite gli endpoint dell’applicazione.
filter := bson.M{
"username": input.Username,
"password": input.Password,
}
Evita:
// Dangerous: allows injection of operators
_ = json.NewDecoder(req.Body).Decode(&filter)
3. Blocca l’iniezione di operatori MongoDB
Impedisci che l’input controllato dall’utente funzioni come operatori di query o costrutti logici. Tratta tutti i dati dell’utente come valori letterali, non come componenti eseguibili della query.
Invece di:
filter := bson.M{
"status": input.Status,
}
Usa:
filter := bson.M{
"status": bson.M{"$eq": input.Status},
}
Questo approccio impone un confronto per uguaglianza rigoroso e impedisce l’esecuzione di operatori iniettati come $ne, $in o $regex o altri modificatori logici che potrebbero aggirare la logica di query prevista.
4. Usa il principio del privilegio minimo per gli utenti del database
Configura le connessioni al database dell’applicazione con account che possiedano solo i permessi minimi necessari per le operazioni previste. Evita di usare credenziali di livello admin nelle configurazioni applicative per prevenire potenziali danni in caso di injection riuscite.
5. Monitora e testa
Implementa un logging completo per rilevare pattern di query insoliti e stabilisci protocolli regolari di test di sicurezza. Verifica la sicurezza degli endpoint tentando attacchi di injection con payload malformati come:
{ "$ne": null }
Verifica che l’applicazione lo rifiuti.
Casi di test da considerare:
- Oggetti invece di stringhe: { "username": { "$ne": null } }
- Array invece di scalari: { "roles": [ { "$gt": "" } ] }
- Operatori come valori di query: { "field": { "$regex": ".*" } }
Checklist di riepilogo
- Validazione degli input: impone tipi di dato rigorosi, valida i formati e rifiuta campi inattesi.
- Costruzione delle query: costruisci tutte le query utilizzando schemi predefiniti e template controllati dall’applicazione
- Protezione dagli operatori: blocca gli operatori MongoDB nell’input utente racchiudendo i valori in $eq o usando API tipizzate.
- JavaScript lato server: elimina le clausole $where e disabilita l’esecuzione di JavaScript nelle configurazioni di produzione.
- Controllo accessi DB: configura le connessioni applicative con i permessi minimi necessari, evitando credenziali amministrative.
- Testing e monitoraggio: esegui regolarmente test di injection e implementa il logging per pattern di query anomali.
Considerazioni finali
Le vulnerabilità di NoSQL injection sono completamente prevenibili con pratiche di coding sicuro disciplinate. Implementando una validazione rigorosa degli input, la costruzione strutturata delle query e controlli di sicurezza defense-in-depth, gli sviluppatori possono mantenere flessibilità e prestazioni dell’applicazione eliminando al contempo i vettori di attacco basati su injection.
Il successo richiede di comprendere il flusso dati completo della tua applicazione—dall’analisi iniziale della richiesta fino all’esecuzione finale della query. Adotta un approccio zero-trust verso tutte le fonti di input, implementa la validazione ai confini del sistema e dai priorità ai principi di programmazione difensiva in tutto il tuo stack tecnologico.
FAQ
Perché la NoSQL injection è un rischio se MongoDB usa BSON invece di stringhe
L’injection può comunque avvenire prima che l’oggetto BSON sia costruito. Se l’input utente viene analizzato in tipi flessibili come map[string]interface{} o interface{} senza validazione, gli aggressori possono iniettare operatori MongoDB come $ne o $or e manipolare la logica della query.
È sicuro decodificare direttamente il body di una richiesta in un bson.M o map[string]interface{}?
No. Questo permette agli utenti di iniettare operatori di query arbitrari. Mappa sempre i dati della richiesta a tipi rigidi (ad es. struct di Go) e valida l’input prima di costruire le query.
Cosa c’è di sbagliato nell’usare $where se l’input è validato?
Anche se l’input è parzialmente validato, $where esegue JavaScript sul server. Qualsiasi errore di escaping o validazione può portare a code injection. È più sicuro evitare del tutto $where o disabilitare lo scripting nella configurazione di MongoDB.
Come posso testare se il mio endpoint è vulnerabile alla NoSQL injection?
Prova a iniettare payload come:
{ "username": { "$ne": null } }
Se l’endpoint restituisce una risposta valida o aggira la logica (ad es. login), l’input non è correttamente validato o vincolato.
Qual è il modo migliore per costruire query in Go in modo sicuro?
Usa struct tipizzate per gli input, evita di decodificare in mappe generiche e racchiudi i valori in $eq quando necessario. Non permettere mai all’utente di definire operatori o iniettare parti della query direttamente.

