A segurança do MongoDB não é totalmente garantida simplesmente configurando certificados de autenticação ou criptografando os dados. Alguns invasores vão “ir além” brincando com os parâmetros recebidos em solicitações HTTP que são usadas como parte do processo de consulta do banco de dados.
Os bancos de dados SQL são os mais vulneráveis a esse tipo de ataque, mas a injeção externa também é possível em DBMs NoSQL, como o MongoDB. Na maioria dos casos, as injeções externas acontecem como resultado de uma concatenação insegura de strings ao criar consultas.
O que é um ataque de injeção externa?
Injeção de código é basicamente a integração de dados não validados (vetor não mitigado) em um programa vulnerável que, quando executado, leva a um acesso desastroso ao seu banco de dados; ameaçando sua segurança.
Quando variáveis não limpas são passadas para uma consulta do MongoDB, elas quebram a estrutura de orientação da consulta do documento e às vezes são executadas como o próprio código javascript. Este é frequentemente o caso ao passar props diretamente do módulo body-parser para o servidor Nodejs. Portanto, um invasor pode inserir facilmente um objeto Js onde você esperaria uma string ou número, obtendo resultados indesejados ou manipulando seus dados.
Considere os dados abaixo na coleção de um aluno.
{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}
Digamos que seu programa precise buscar todos os alunos com idade igual a 20, você escreveria um código como este...
app.get(‘/:age’, function(req, res){
db.collections(“students”).find({age: req.params.age});
})
Você terá enviado um objeto JSON em sua solicitação http como
{age: 20}
Isso retornará todos os alunos com idade igual a 20 como resultado esperado e, neste caso, apenas {username:'John Doc', email:'[email protected]', age:20} .
Agora, digamos que um invasor envie um objeto em vez de um número, ou seja, {‘$gt:0’};
A consulta resultante será:
db.collections(“alunos”).find({idade:{‘$gt:0’}); que é uma consulta válida que, após a execução, retornará todos os alunos dessa coleção. O invasor tem a chance de agir em seus dados de acordo com suas intenções maliciosas. Na maioria dos casos, um invasor injeta um objeto personalizado que contém comandos do MongoDB que permitem acessar seus documentos sem o procedimento adequado.
Alguns comandos do MongoDB executam código Javascript dentro do mecanismo de banco de dados, um risco potencial para seus dados. Alguns desses comandos são '$where', '$group' e 'mapReduce'. Para versões anteriores ao MongoDB 2.4, o código Js tem acesso ao objeto db de dentro da consulta.
Proteções Nativas do MongoDB
O MongoDB utiliza os dados BSON (JSON binário) para suas consultas e documentos, mas em alguns casos pode aceitar expressões JSON e Js não serializadas (como as mencionadas acima). A maioria dos dados passados para o servidor está no formato de uma string e pode ser alimentada diretamente em uma consulta do MongoDB. O MongoDB não analisa seus dados, portanto, evitando riscos potenciais que podem resultar da integração direta de parâmetros.
Se uma API envolve a codificação de dados em um texto formatado e esse texto precisa ser analisado, há o potencial de criar desacordo entre o chamador do servidor e o chamador do banco de dados sobre como essa string será analisada . Se os dados forem acidentalmente interpretados incorretamente como metadados, o cenário pode representar ameaças de segurança aos seus dados.
Exemplos de injeções externas do MongoDB e como lidar com elas
Vamos considerar os dados abaixo em uma coleção de alunos.
{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}
Injeção usando o operador $ne (diferente)
Se eu quiser devolver o documento com nome de usuário e senha fornecidos a partir de uma solicitação o código será:
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);
});
});
Se recebermos a solicitação abaixo
POST https://localhost/students HTTP/1.1
Content-Type: application/json
{
"username": {"$ne": null},
"password": {"$ne": null}
}
A consulta definitivamente retornará o primeiro aluno neste caso, pois seu nome de usuário e senha não são considerados nulos. Isso não está de acordo com os resultados esperados.
Para resolver isso, você pode usar:
módulo mongo-sanitize que impede que qualquer chave que comece com ‘$’ seja passada para o mecanismo de consulta MongoDB.
Instale o módulo primeiro
npm install mongo-sanitize
var sanitize = require(‘mongo-sanitize’);
var query = {
username: req.body.username,
password: req.body.password
}
Usando o mangusto para validar seus campos de esquema de forma que, se ele esperar uma string e receber um objeto, a consulta lançará um erro. No nosso caso acima o valor nulo será convertido em uma string “” que literalmente não tem impacto.
Injeção usando o operador $where
Este é um dos operadores mais perigosos. Ele permitirá que uma string seja avaliada dentro do próprio servidor. Por exemplo, para buscar alunos cuja idade está acima de um valor Y, a consulta será
var query = {
$where: “this.age > ”+req.body.age
}
db.collection(students).findOne(query, function (err, student) {
res(student);
});
Usar o módulo de sanitização não ajudará neste caso se tivermos um '0; return true' porque o resultado retornará todos os alunos em vez daqueles cuja idade é maior que um determinado valor. Outras possíveis strings que você pode receber são ‘\’; return \ ‘\’ ==\’’ ou this.email ===‘’;return ‘’ ==‘’. Essa consulta retornará todos os alunos em vez de apenas aqueles que correspondem à cláusula.
A cláusula $where deve ser evitada. Além do contratempo descrito também reduz o desempenho porque não é otimizado para usar índices.
Há também uma grande possibilidade de passar uma função na cláusula $where e a variável não estará acessível no escopo do MongoDB, portanto, pode resultar na falha do seu aplicativo. Ou seja
var query = {
$where: function() {
return this.age > setValue //setValue is not defined
}
}
Você também pode usar os operadores $eq, $lt, $lte, $gt, $gte.
Protegendo-se da injeção externa do MongoDB
Aqui estão três coisas que você pode fazer para se manter protegido...
- Validar os dados do usuário. Olhando para trás em como a expressão $where pode ser usada para acessar seus dados, é aconselhável sempre validar o que os usuários enviam ao seu servidor.
- Use o conceito do validador JSON para validar seu esquema junto com o módulo mangusto.
- Projete suas consultas de forma que o código Js não tenha acesso total ao código do seu banco de dados.
Conclusão
A injeção externa também é possível com o MongoDB. É frequentemente associado a dados de usuário não validados entrando em consultas do MongoDB. É sempre importante detectar e evitar a injeção de NoSQL testando quaisquer dados que possam ser recebidos pelo seu servidor. Se negligenciado, isso pode ameaçar a segurança dos dados do usuário. O procedimento mais importante é validar seus dados em todas as camadas envolvidas.