MongoDB-säkerheten är inte helt garanterad genom att bara konfigurera autentiseringscertifikat eller kryptera data. Vissa angripare kommer att "gå den extra milen" genom att leka med de mottagna parametrarna i HTTP-förfrågningar som används som en del av databasens frågeprocess.
SQL-databaser är mest sårbara för den här typen av attacker, men extern injektion är också möjlig i NoSQL DBM:er som MongoDB. I de flesta fall sker externa injektioner som ett resultat av en osäker sammanlänkning av strängar när du skapar frågor.
Vad är en extern injektionsattack?
Kodinjektion är i grunden att integrera ovaliderad data (oförbättrad vektor) i ett sårbart program som när det körs leder till katastrofal åtkomst till din databas; hotar dess säkerhet.
När osanifierade variabler skickas till en MongoDB-fråga bryter de dokumentfrågeorienteringsstrukturen och exekveras ibland som själva javascript-koden. Detta är ofta fallet när rekvisita skickas direkt från body-parser-modulen för Nodejs-servern. Därför kan en angripare enkelt infoga ett Js-objekt där du förväntar dig en sträng eller ett nummer, och därigenom få oönskade resultat eller genom att manipulera din data.
Tänk på data nedan i en elevs samling.
{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}
Låt oss säga att ditt program måste hämta alla elever vars ålder är lika med 20, du skulle skriva en kod så här...
app.get(‘/:age’, function(req, res){
db.collections(“students”).find({age: req.params.age});
})
Du kommer att ha skickat ett JSON-objekt i din http-förfrågan som
{age: 20}
Detta returnerar alla elever vars ålder är lika med 20 som förväntat resultat och i detta fall endast {användarnamn:'John Doc', email:'[email protected]', age:20} .
Låt oss nu säga att en angripare skickar ett objekt istället för ett nummer, dvs {‘$gt:0’};
Den resulterande frågan blir:
db.collections(“students”).find({age:{‘$gt:0’}); vilket är en giltig fråga som vid körning returnerar alla elever i den samlingen. Angriparen har en chans att agera på din data enligt sina skadliga avsikter. I de flesta fall injicerar en angripare ett anpassat objekt som innehåller MongoDB-kommandon som gör att de kan komma åt dina dokument utan rätt procedur.
Vissa MongoDB-kommandon kör Javascript-kod i databasmotorn, en potentiell risk för dina data. Några av dessa kommandon är '$where', '$group' och 'mapReduce'. För versioner före MongoDB 2.4 har Js-kod åtkomst till db-objektet från frågan.
MongoDB Naitivt skydd
MongoDB använder BSON-data (binär JSON) för både sina frågor och dokument, men i vissa fall kan den acceptera oserialiserade JSON- och Js-uttryck (som de som nämns ovan). De flesta data som skickas till servern är i formatet av en sträng och kan matas direkt in i en MongoDB-fråga. MongoDB analyserar inte sina data och undviker därför potentiella risker som kan uppstå från att direkta parametrar integreras.
Om ett API involverar kodning av data i en formaterad text och den texten behöver tolkas, har det potential att skapa oenighet mellan serverns anropare och databasens anropare om hur den strängen ska tolkas . Om data av misstag misstolkas som metadata kan scenariot potentiellt utgöra säkerhetshot mot din data.
Exempel på MongoDB externa injektioner och hur man hanterar dem
Låt oss överväga data nedan i en elevsamling.
{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}
Injektion med $ne (inte lika) operatör
Om jag vill returnera dokumentet med användarnamn och lösenord från en begäran kommer koden att vara:
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);
});
});
Om vi får begäran nedan
POST https://localhost/students HTTP/1.1
Content-Type: application/json
{
"username": {"$ne": null},
"password": {"$ne": null}
}
Frågan kommer definitivt att returnera den första studenten i det här fallet eftersom hans användarnamn och lösenord inte värderas som null. Detta är inte enligt de förväntade resultaten.
För att lösa detta kan du använda:
mongo-sanitize-modul som stoppar alla nycklar som börjar med '$' från att skickas till MongoDB-frågemotorn.
Installera modulen först
npm install mongo-sanitize
var sanitize = require(‘mongo-sanitize’);
var query = {
username: req.body.username,
password: req.body.password
}
Använda mongoose för att validera dina schemafält så att om den förväntar sig en sträng och tar emot ett objekt, kommer frågan att ge ett fel. I vårt fall ovan kommer nullvärdet att omvandlas till en sträng "" som bokstavligen inte har någon inverkan.
Injektion med $where-operatören
Detta är en av de farligaste operatörerna. Det gör att en sträng kan utvärderas inuti själva servern. Till exempel, för att hämta elever vars ålder är över ett värde Y, kommer frågan att vara
var query = {
$where: “this.age > ”+req.body.age
}
db.collection(students).findOne(query, function (err, student) {
res(student);
});
Att använda saneringsmodulen hjälper inte i det här fallet om vi har en '0; return true’ eftersom resultatet kommer att returnera alla elever snarare än de vars ålder är högre än något givet värde. Andra möjliga strängar du kan ta emot är '\'; returnera \ '\' ==\'' eller this.email ===''; returnera '' ==''. Den här frågan kommer att returnera alla elever snarare än bara de som matchar satsen.
$where-satsen bör absolut undvikas. Förutom det skisserade bakslaget minskar det också prestandan eftersom den inte är optimerad för att använda index.
Det finns också en stor möjlighet att skicka en funktion i $where-satsen och variabeln kommer inte att vara tillgänglig i MongoDB-omfånget, vilket kan leda till att din applikation kraschar. Dvs
var query = {
$where: function() {
return this.age > setValue //setValue is not defined
}
}
Du kan också använda operatorerna $eq, $lt, $lte, $gt, $gte istället.
Skydda dig själv från MongoDB extern injektion
Här är tre saker du kan göra för att skydda dig...
- Verifiera användardata. När du ser tillbaka på hur uttrycket $where kan användas för att komma åt din data, är det lämpligt att alltid validera vad användare skickar till din server.
- Använd JSON-valideringskonceptet för att validera ditt schema tillsammans med mongoose-modulen.
- Designa dina frågor så att Js-koden inte har full åtkomst till din databaskod.
Slutsats
Extern injektion är också möjlig med MongoDB. Det är ofta associerat med ovaliderad användardata som kommer in i MongoDB-frågor. Det är alltid viktigt att upptäcka och förhindra NoSQL-injektion genom att testa all data som kan tas emot av din server. Om det försummas kan detta hota säkerheten för användardata. Den viktigaste proceduren är att validera dina data på alla inblandade lager.