Найдите NoSQL-инъекцию в логине на Mongoose
Этот маршрут логина на Express передаёт тело запроса прямо в запрос MongoDB через Mongoose. Определите уязвимость и как её эксплуатируют, затем опишите исправление.
Ограничения:
req.body— распарсенный JSON, полностью контролируемый атакующимUser.findOneпринимает любой переданный объект
router.post("/login", async (req, res) => {
const login = req.body.username; // ожидается строка
const user = await User.findOne(login); // объект уходит как есть
// ...далее проверка пароля
});
Определите причину.
NoSQL-инъекция из-за неверной обработки типа — username не приводится к строке, поэтому атакующий шлёт объект из операторов MongoDB, меняющий семантику запроса и обходящий поиск. Фикс: привести к строке и искать по явному полю вместо передачи сырого объекта запроса.
- ✗Считать NoSQL неуязвимым к инъекции из-за отсутствия SQL-синтаксиса
- ✗Передавать сырой объект запроса в драйвер БД без приведения типов
- ✗Путать это с классической SQL-инъекцией или с гонкой
- →Как оператор $regex в поле пароля позволяет посимвольно его перебрать?
- →Почему приведение к строке у источника надёжнее фильтрации операторов?
Уязвимость
req.body.username уходит в findOne без приведения типа. Если тело — JSON, клиент может прислать вместо строки объект с операторами MongoDB:
const login = req.body.username; // ожидалась строка
const user = await User.findOne(login); // а пришёл объект
Пейлоад {"username": {"$ne": null}} вернёт первого попавшегося пользователя; $regex в поле пароля позволяет перебирать его посимвольно. Это NoSQL-инъекция через неверную обработку типа.
Исправление
Никогда не передавайте сырой объект запроса в драйвер. Приведите к строке и ищите по явному полю:
const login = String(req.body.username);
const user = await User.findOne({ username: login });
✅ Приведение типа у источника гарантирует, что в запрос попадёт строка, а не объект операторов.