Статьи

Защита от атак инъекций селектора запросов

Если вы еще не сталкивались с постом Петко Петкова о инъекционных атаках на MongoDB и NodeJS, его, безусловно, стоит внимательно прочитать . В этой статье он объясняет довольно простой эксплойт, который, как я подозреваю, влияет на значительное число приложений, включая некоторые, которые я реализовал.

Основная идея эксплойта Petko заключается в том, что, как правило, когда вы хотите получить все документы, где имя пользователя совпадает с предоставленным пользователем именем пользователя, вы можете сделать что-то вроде этого:

User.findOne({ username: req.body.username }, function(err, user) {
  // Handler code here
});

Однако предположим, что вы раскрыли API на основе JSON, а я злонамеренный пользователь, который отправляет вам следующее тело JSON:

{ username: { $gt: "" } }

Запрос, который будет отправлен в MongoDB, будет выглядеть следующим образом:

{ username: { $gt: "" } }

Предполагая, что ваши имена пользователей являются строками, этот запрос вернет случайного пользователя!

Даже если вы используете кодировку URL вместо JSON для своего API, вы не можете быть в безопасности. Промежуточное программное обеспечение синтаксического анализатора тела ExpressJS по умолчанию использует модуль qs для анализа тел HTTP-запросов в кодировке URL. Модуль qs предназначен для анализа строк в кодировке URL таким образом, чтобы упростить декодирование объектов, поэтому анализ строки username [$ gt] = дает вам вложенный объект {username: {$ gt: undefined}} . Это действительно плохие новости для медведей.

К счастью, от атак с использованием селектора запросов довольно легко защититься, поэтому не нужно выбрасывать свой Express JSON API из окна. Вот две стратегии, чтобы убедиться, что вы не уязвимы.

Удалите ключи, начинающиеся с $, из пользовательского ввода

Одним из основных аспектов использования Petko является то, что в приведенном выше примере MongoDB определяет селектор запросов путем сканирования объекта req.body.username на предмет ключа, соответствующего селектору запросов . Есть два способа избежать этого. Первое и, вероятно, наиболее очевидное, это убедиться, что req.body.username является строкой, а не объектом. Функция JavaScript toString должна быть достаточной:

User.findOne({ username: (req.body.username || "").toString(10) }, function(err, user) {
  // Handler code here
});

Однако в некоторых случаях вам может потребоваться выполнить запрос к предоставленным пользователем объектам, поэтому приведения к строке недостаточно. Поскольку все селекторы запросов MongoDB начинаются с $, вы можете проверить, является ли req.body.username объектом, и, если это так, удалить любые ключи из объекта, начинающегося с $. Я собрал действительно простой модуль npm под названием mongo-sanitize (смотрите его на Github ), который сделает это за вас, если вы не хотите реализовывать это самостоятельно.

var sanitize = require('mongo-sanitize');
 
// The sanitize function will strip out any keys that start with '$' in the input,
// so you can pass it to MongoDB without worrying about malicious users overwriting
// query selectors.
var clean = sanitize(req.params.username);
 
Users.findOne({ name: clean }, function(err, doc) {
  // ...
});

Если этот подход не работает для вас по любой причине, не волнуйтесь, есть другой способ.

Явно указывать селектор запросов при запросах с ненадежными данными

Другой смысл эксплойта Petko заключается в том, что, как правило, вы не указываете селектор запросов, когда хотите найти документ, имя пользователя которого точно совпадает с пользовательским вводом. На самом деле, MongoDB пока не имеет полностью поддерживаемого селектора запросов $ eq (хотя над этим работает команда главного сервера ). Однако вместо $ eq вы можете использовать селектор $ in :

User.findOne({ username: { $in: [req.body.username] } }, function(err, user) {
  // Handler code here
});

Это немного более многословно, но если злонамеренный пользователь попробует атаку с использованием селектора запроса, переданный запрос будет выглядеть следующим образом:

{ username: { $in: [{ $gt: "" }] } }

Предполагая, что ваши имена пользователей были строками, этот запрос не даст результатов, как ожидалось.

Вывод

Атаки с использованием селектора запросов довольно коварны и легко уязвимы, особенно если вы успешно внедряете API-интерфейсы JSON REST. К счастью, используя один из вышеперечисленных принципов, используя mongo-sanitize или явно указав селектор запросов для ненадежных данных, вы можете избежать ловушки внедрения селектора запросов, не отказываясь от простоты использования JSON API. Если вы хотите получить более подробную информацию о защите вашего приложения MongoDB, ознакомьтесь с контрольным списком безопасности и публикацией в блоге MongoDB по дизайну и настройке безопасности .