Prevenir injeção de SQL em Node.js


87

É possível evitar injeções de SQL em Node.js (de preferência com um módulo) da mesma forma que o PHP preparou instruções que protegem contra eles.

Se sim, como? Se não, quais são alguns exemplos que podem ignorar o código que forneci (veja abaixo).


Algum Contexto:

Estou fazendo um aplicativo da web com uma pilha de back-end que consiste em Node.js + MySql usando o módulo node-mysql . Do ponto de vista da usabilidade, o módulo é ótimo, mas ainda não implementou algo semelhante às instruções preparadas do PHP (embora esteja ciente de que está no todo ).

Do meu entendimento, a implementação de instruções preparadas em PHP, entre outras coisas, ajudou muito na prevenção de injeções de SQL. Estou preocupado, porém, que meu aplicativo node.js possa estar aberto a ataques semelhantes, mesmo com o escape de string fornecido por padrão (como no trecho de código abaixo).

node-mysql parece ser o conector mysql mais popular para node.js, então eu queria saber o que outras pessoas podem estar fazendo (se algo) para explicar este problema - ou se é mesmo um problema com node.js para começar (não tenho certeza de como isso não seria, uma vez que a entrada do lado do usuário / cliente está envolvida).

Devo mudar para node-mysql-native por enquanto, já que ele fornece instruções preparadas?Estou hesitante em fazer isso, porque não parece ser tão ativo quanto node-mysql (embora isso possa significar apenas que está completo).

Aqui está um trecho do código de registro do usuário, que usa o módulo sanitizer , junto com a sintaxe de instrução preparada do node-mysql (que, como mencionei acima, faz escape de caractere), para evitar scripts entre sites e injeções de sql, respectivamente:

// Prevent xss
var clean_user = sanitizer.sanitize(username);

// assume password is hashed already
var post = {Username: clean_user, Password: hash};

// This just uses connection.escape() underneath
var query = connection.query('INSERT INTO users SET ?', post,
   function(err, results)
   {
       // Can a Sql injection happen here?
   });

Respostas:


60

A node-mysqlbiblioteca executa o escape automaticamente quando usada como você já está fazendo. Consulte https://github.com/felixge/node-mysql#escaping-query-values


3
Como mencionei em minha postagem, estou ciente de que a biblioteca escapa caracteres, mas estou mais preocupado com as implicações de segurança se eu não mudar para uma biblioteca que implementou instruções preparadas, ou seja, há uma injeção de SQL que pode ocorrer com o que Estou fazendo atualmente?
funseiki

2
O escape de caracteres evita a injeção de SQL. As injeções ocorrem quando os caracteres não são escapados e usuários mal-intencionados podem explorar isso para fechar a consulta e iniciar uma nova para, digamos, eliminar uma tabela ou inserir um registro falso. Com caracteres de escape, isso não é possível. A Wikipedia tem algumas informações adicionais sobre injeção de SQL.
Michael Pratt

4
Mas isso impede todas as injeções de SQL? Esta resposta sugere que não (pelo menos para PHP + MySQL) e implica que as instruções preparadas do PHP o fazem. Novamente, isso está no contexto do PHP.
funseiki

1
De acordo com seu link, isso só funciona em versões desatualizadas do MySQL. Não sei se aquele ataque em particular funciona no Node, mas parece que teve a ver com vulnerabilidades de PHP muito específicas, então meu pressentimento é não. Não estou dizendo que não haja absolutamente nenhuma vulnerabilidade no node-mysql, mas ele já está sendo usado em muitos ambientes de produção. Se você ainda está preocupado com a injeção de SQL, sugiro ir direto ao ponto e experimentar algo como o MongoDB - não é possível fazer uma injeção de SQL se você não estiver usando SQL.
Michael Pratt

1
Parecia assim e a rota do MongoDB é um bom ponto - embora o design atual se prestasse bem a um esquema relacional. Vou esperar para ver se mais alguém tem uma visão sobre as vulnerabilidades de segurança - caso contrário, parece que o consenso é em apenas ficar com node-mysql
funseiki

12

A biblioteca tem uma seção no leia-me sobre como escapar. É Javascript-nativo, então eu não sugiro mudar para node-mysql-native . A documentação declara estas diretrizes para escape:

Edit: node-mysql-native também é uma solução Javascript puro.

  • Números são deixados intocados
  • Booleanos são convertidos em true/ falsestrings
  • Objetos de data são convertidos em YYYY-mm-dd HH:ii:ssstrings
  • Buffers são convertidos em strings hexadecimais, por exemplo X'0fa5'
  • Strings são escapadas com segurança
  • Arrays são transformados em lista, por exemplo, ['a', 'b']transforma-se em'a', 'b'
  • Arrays aninhados são transformados em listas agrupadas (para inserções em massa), por exemplo, [['a', 'b'], ['c', 'd']]transforma-se em('a', 'b'), ('c', 'd')
  • Os objetos são transformados em key = 'val'pares. Objetos aninhados são convertidos em strings.
  • undefined/ nullsão convertidos paraNULL
  • NaNEu Infinityfico como está. O MySQL não oferece suporte a eles e tentar inseri-los como valores irá disparar erros do MySQL até que eles implementem o suporte.

Isso permite que você faça coisas como:

var userId = 5;
var query = connection.query('SELECT * FROM users WHERE id = ?', [userId], function(err, results) {
  //query.sql returns SELECT * FROM users WHERE id = '5'
});

Tanto quanto este:

var post  = {id: 1, title: 'Hello MySQL'};
var query = connection.query('INSERT INTO posts SET ?', post, function(err, result) {
  //query.sql returns INSERT INTO posts SET `id` = 1, `title` = 'Hello MySQL'
});

Além dessas funções, você também pode usar as funções de escape:

connection.escape(query);
mysql.escape(query);

Para escapar dos identificadores de consulta:

mysql.escapeId(identifier);

E como resposta ao seu comentário sobre as declarações preparadas:

Do ponto de vista da usabilidade, o módulo é ótimo, mas ainda não implementou algo semelhante às instruções preparadas do PHP.

As instruções preparadas estão na lista de tarefas desse conector, mas este módulo pelo menos permite que você especifique formatos personalizados que podem ser muito semelhantes às instruções preparadas. Aqui está um exemplo do leia-me:

connection.config.queryFormat = function (query, values) {
  if (!values) return query;
  return query.replace(/\:(\w+)/g, function (txt, key) {
    if (values.hasOwnProperty(key)) {
      return this.escape(values[key]);
    }
    return txt;
  }.bind(this));
};

Isso altera o formato de consulta da conexão para que você possa usar consultas como esta:

connection.query("UPDATE posts SET title = :title", { title: "Hello MySQL" });
//equivalent to
connection.query("UPDATE posts SET title = " + mysql.escape("Hello MySQL");

Obrigado pela resposta - estou ciente do estilo tipo preparado. Por baixo, porém, os personagens estão sendo escapados. Veja: "No entanto, ele realmente usa apenas o mesmo connection.escape ()" . Quanto a não usar node-mysql-native: é com isso que estou lutando. Se node-mysql-native implementa instruções preparadas e suas implementações previnem injeções SQL, não devo fazer a troca até que node-mysql as tenha?
funseiki de

É uma espécie de questão do ovo e da galinha. Não estou desenvolvendo ativamente meu driver porque a maioria das pessoas usa @felixge's. Provavelmente tentarei encontrar algum tempo para transferir as instruções preparadas para o node-mysql, pois isso realmente oferece alguns benefícios de desempenho (e potencialmente torna as injeções de sql mais difíceis). Sinta-se à vontade para comentar / postar problemas se você decidir tentar
Andrey Sidorov

1
@funseiki Tenho certeza de que instruções preparadas seriam a melhor solução, mas tenho certeza de que o escape impedirá injeções de SQL. Uma vez que o próprio módulo é suportado por Joyent, o módulo está ativo e evidentemente completamente verificado. Se este módulo não estivesse pronto para produção, não acho que ele teria uma média de 1000 downloads / dia no mês passado. Observe que node-mysql-native faz 6 meses desde que foi desenvolvido pela última vez, e node-mysql é muito ativo, com várias pessoas trabalhando nele.
hexacianida

@AndreySidorov Obrigado pelo comentário - se eu tentar resolver isso, postarei uma atualização. Eu não acho que será tão cedo, porém, já que não parece que será um animal fácil de manusear (exigirá mais pesquisa do que tenho tempo para fazer). Obrigado também por fazer esse driver - vocês são a razão pela qual o Node.js facilita a execução rápida de aplicativos
funseiki

@hexacyanide Como o node-mysql é tão popular, eu esperava poder obter uma resposta dos membros da comunidade sobre os problemas de segurança que eles podem ter encontrado (ou evitado), bem como um argumento convincente sobre por que a abordagem de escape de personagem atual é segura o suficiente para seu código.
funseiki

12

Sei que esta é uma postagem mais antiga, mas parece que uma resposta nunca foi marcada, então vou jogá-la fora.

Com relação a testar se um módulo que você está utilizando é seguro ou não, existem vários caminhos que você pode seguir. Falarei sobre os prós / contras de cada um para que você possa tomar uma decisão mais informada.

Atualmente não há nenhuma vulnerabilidade para o módulo que você está utilizando, no entanto, isso muitas vezes pode levar a uma falsa sensação de segurança, pois pode haver uma vulnerabilidade explorando o módulo / pacote de software que você está usando e você não seria alertado a um problema até que o fornecedor aplique uma correção / patch.

  1. Para se manter atualizado sobre as vulnerabilidades, você precisará seguir listas de e-mail, fóruns, IRC e outras discussões relacionadas a hackers. PRO: Muitas vezes, você pode ficar ciente de problemas potenciais dentro de uma biblioteca antes que um fornecedor seja alertado ou emitido uma correção / patch para remediar a avenida potencial de ataque ao seu software. CON: Isso pode consumir muito tempo e muitos recursos. Se você seguir esse caminho, um bot usando feeds RSS, análise de log (logs de bate-papo IRC) e / ou um web scrapper usando frases-chave (neste caso node-mysql-native) e notificações podem ajudar a reduzir o tempo gasto rastreando esses recursos.

  2. Criar um fuzzer, use um difusor ou outro quadro de vulnerabilidade, como Metasploit , SqlMap etc, para teste de ajuda para problemas que o vendedor não pode ter olhado para. PRO: Este pode ser um método certeiro de garantir a um nível aceitável se o módulo / software que você está implementando é seguro para acesso público ou não. CON: Isso também se torna demorado e caro. O outro problema resultará de falsos positivos, bem como da revisão não instruída dos resultados, onde o problema reside, mas não é percebido.

A segurança real e a segurança de aplicativos em geral podem consumir muito tempo e muitos recursos. Uma coisa que os gerentes sempre usarão é uma fórmula para determinar a eficácia de custo (mão de obra, recursos, tempo, pagamento, etc.) de realizar as duas opções acima.

De qualquer forma, eu percebo que esta não é uma resposta 'sim' ou 'não' que você esperava, mas não acho que alguém possa dar isso a você até que faça uma análise do software em questão.


3

Eu sei que esta questão é antiga, mas para qualquer pessoa interessada, o Mysql nativo está desatualizado, então se tornou o MySQL2 que é um novo módulo criado com a ajuda da equipe do módulo original do MySQL. Este módulo tem mais recursos e acho que tem o que você quer, pois tem instruções preparadas (usando.execute ()) como no PHP para mais segurança.

Também é muito ativo (a última mudança foi de 2 a 1 dias). Não tentei antes, mas acho que é o que você quer e muito mais.


-1

A maneira mais fácil é lidar com todas as suas interações de banco de dados em seu próprio módulo que você exporta para suas rotas. Se sua rota não tem contexto do banco de dados, o SQL não pode tocá-la de qualquer maneira.


Isso realmente não responde à pergunta da OP sobre como higienizar.
Christopher
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.