Bezpieczeństwo MongoDB nie jest w pełni gwarantowane przez zwykłą konfigurację certyfikatów uwierzytelniających lub szyfrowanie danych. Niektórzy atakujący pójdą o krok dalej, bawiąc się otrzymanymi parametrami w żądaniach HTTP, które są wykorzystywane jako część procesu zapytania bazy danych.
Bazy danych SQL są najbardziej podatne na tego typu ataki, ale zewnętrzne wstrzyknięcie jest również możliwe w bazach danych NoSQL, takich jak MongoDB. W większości przypadków zewnętrzne wstrzyknięcia mają miejsce w wyniku niebezpiecznego łączenia ciągów podczas tworzenia zapytań.
Co to jest atak z zewnętrznego wstrzyknięcia?
Wstrzykiwanie kodu to w zasadzie integracja niesprawdzonych danych (nieograniczonych wektorów) z podatnym programem, który po uruchomieniu prowadzi do katastrofalnego dostępu do bazy danych; zagrażające jego bezpieczeństwu.
Gdy nieoczyszczone zmienne są przekazywane do zapytania MongoDB, łamią one strukturę orientacji zapytania dokumentu i czasami są wykonywane jako sam kod javascript. Dzieje się tak często przy przekazywaniu props bezpośrednio z modułu body-parser dla serwera Nodejs. Dlatego osoba atakująca może łatwo wstawić obiekt Js w miejscu, w którym można się spodziewać ciągu lub liczby, uzyskując w ten sposób niepożądane wyniki lub manipulując danymi.
Rozważ poniższe dane w kolekcji ucznia.
{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}
Powiedzmy, że Twój program musi pobierać wszystkich uczniów w wieku 20 lat. Napisałbyś taki kod...
app.get(‘/:age’, function(req, res){
db.collections(“students”).find({age: req.params.age});
})
W żądaniu http prześlesz obiekt JSON jako
{age: 20}
Zwróci to wszystkich uczniów w wieku 20 lat jako oczekiwany wynik i tylko w tym przypadku {username:'John Doc', email:'[email protected]', age:20} .
Załóżmy teraz, że atakujący przesyła obiekt zamiast liczby, tj. {‘$gt:0’};
Wynikowe zapytanie to:
db.collections("studenci").find({wiek:{'$gt:0'}); które jest prawidłowym zapytaniem, które po wykonaniu zwróci wszystkich uczniów w tej kolekcji. Atakujący ma szansę działać na Twoich danych zgodnie ze swoimi złośliwymi intencjami. W większości przypadków atakujący wstrzykuje niestandardowy obiekt, który zawiera polecenia MongoDB, które umożliwiają mu dostęp do Twoich dokumentów bez odpowiedniej procedury.
Niektóre polecenia MongoDB wykonują kod JavaScript w silniku bazy danych, co stanowi potencjalne zagrożenie dla danych. Niektóre z tych poleceń to „$where”, „$group” i „mapReduce”. W przypadku wersji wcześniejszych niż MongoDB 2.4 kod Js ma dostęp do obiektu db z poziomu zapytania.
Natywna ochrona MongoDB
MongoDB wykorzystuje dane BSON (binarny JSON) zarówno dla swoich zapytań, jak i dokumentów, ale w niektórych przypadkach może akceptować niezserializowane wyrażenia JSON i Js (takie jak te wymienione powyżej). Większość danych przekazywanych do serwera jest w formacie ciągu i można je wprowadzić bezpośrednio do zapytania MongoDB. MongoDB nie analizuje swoich danych, dzięki czemu unika się potencjalnych zagrożeń, które mogą wynikać z integracji bezpośrednich parametrów.
Jeśli interfejs API wymaga kodowania danych w sformatowanym tekście i tekst ten musi zostać przeanalizowany, może to spowodować niezgodność między wywołującym serwer a wywoływaną bazą danych co do tego, jak ten ciąg ma być analizowany . Jeśli dane zostaną przypadkowo błędnie zinterpretowane jako metadane, scenariusz może potencjalnie stanowić zagrożenie dla bezpieczeństwa Twoich danych.
Przykłady zewnętrznych wstrzyknięć MongoDB i sposoby ich obsługi
Rozważmy poniższe dane w zbiorze uczniów.
{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}
Wstrzykiwanie za pomocą operatora $ne (nie równego)
Jeśli chcę zwrócić dokument z nazwą użytkownika i hasłem podanym w żądaniu, kod będzie wyglądał następująco:
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);
});
});
Jeśli otrzymamy poniższe żądanie
POST https://localhost/students HTTP/1.1
Content-Type: application/json
{
"username": {"$ne": null},
"password": {"$ne": null}
}
W tym przypadku zapytanie z pewnością zwróci pierwszego ucznia, ponieważ jego nazwa użytkownika i hasło nie mają wartości NULL. Nie jest to zgodne z oczekiwanymi wynikami.
Aby rozwiązać ten problem, możesz użyć:
moduł mongo-sanitize, który zatrzymuje każdy klucz zaczynający się od „$” przed przekazaniem do silnika zapytań MongoDB.
Najpierw zainstaluj moduł
npm install mongo-sanitize
var sanitize = require(‘mongo-sanitize’);
var query = {
username: req.body.username,
password: req.body.password
}
Używanie mangusty do sprawdzania poprawności pól schematu tak, że jeśli oczekuje ciągu i odbierze obiekt, zapytanie wygeneruje błąd. W naszym przypadku powyżej wartość null zostanie przekonwertowana na ciąg znaków „”, który dosłownie nie ma żadnego wpływu.
Wstrzykiwanie za pomocą operatora $where
To jeden z najniebezpieczniejszych operatorów. Pozwoli to na ocenę ciągu wewnątrz samego serwera. Na przykład, aby pobrać uczniów, których wiek przekracza wartość Y, zapytanie będzie wyglądać tak:
var query = {
$where: “this.age > ”+req.body.age
}
db.collection(students).findOne(query, function (err, student) {
res(student);
});
Skorzystanie z modułu sanitize nie pomoże w tym przypadku, jeśli mamy ‘0; return true’, ponieważ wynik zwróci wszystkich uczniów, a nie tych, których wiek jest większy niż podana wartość. Inne możliwe ciągi, które możesz otrzymać, to „\”; return \ ‘\’ ==\’’ lub this.email ===‘’;return ‘’ ==‘’. To zapytanie zwróci wszystkich uczniów, a nie tylko tych, którzy pasują do klauzuli.
Należy zdecydowanie unikać klauzuli $where. Oprócz opisanej wady zmniejsza to również wydajność, ponieważ nie jest zoptymalizowany do korzystania z indeksów.
Istnieje również duża możliwość przekazania funkcji w klauzuli $where, a zmienna nie będzie dostępna w zakresie MongoDB, co może spowodować awarię aplikacji. To znaczy
var query = {
$where: function() {
return this.age > setValue //setValue is not defined
}
}
Możesz również użyć operatorów $eq, $lt, $lte, $gt, $gte.
Ochrona się przed zewnętrznym wstrzyknięciem MongoDB
Oto trzy rzeczy, które możesz zrobić, aby zapewnić sobie ochronę...
- Zweryfikuj dane użytkownika. Patrząc wstecz na to, jak wyrażenie $where może być używane do uzyskiwania dostępu do danych, zaleca się, aby zawsze sprawdzać, co użytkownicy wysyłają na serwer.
- Użyj koncepcji walidatora JSON, aby sprawdzić poprawność schematu wraz z modułem mangusta.
- Zaprojektuj swoje zapytania tak, aby kod Js nie miał pełnego dostępu do kodu Twojej bazy danych.
Wnioski
Z MongoDB możliwe są również wtryski zewnętrzne. Często wiąże się to z dostaniem się niesprawdzonych danych użytkownika do zapytań MongoDB. Zawsze ważne jest wykrywanie i zapobieganie wstrzykiwaniu NoSQL poprzez testowanie wszelkich danych, które mogą być odbierane przez Twój serwer. W przypadku zaniedbania może to zagrozić bezpieczeństwu danych użytkownika. Najważniejszą procedurą jest sprawdzenie poprawności danych we wszystkich zaangażowanych warstwach.