101

是否有可能以与 PHP 具有防范它们的 Prepared Statements 相同的方式防止 Node.js 中的 SQL 注入(最好使用模块)。

如果是这样,怎么做?如果没有,有哪些示例可能会绕过我提供的代码(见下文)。


一些背景:

我正在使用node-mysql模块制作一个由 Node.js + MySql 组成的后端堆栈的 Web 应用程序。从可用性的角度来看,这个模块很棒,但是它还没有实现类似于 PHP 的Prepared Statements的东西(尽管我知道它在todo上)。

据我了解,PHP 对预准备语句的实现等对防止 SQL 注入有很大帮助。不过,我担心我的 node.js 应用程序可能会受到类似的攻击,即使默认提供了字符串转义(如下面的代码片段所示)。

node-mysql 似乎是 node.js 最流行的 mysql 连接器,所以我想知道其他人可能会做什么(如果有的话)来解决这个问题 - 或者它是否甚至是 node.js 开始的问题(不知道这是怎么回事,因为涉及到用户/客户端输入)。

我是否应该暂时切换到node-mysql-native,因为它确实提供了准备好的语句?我对此犹豫不决,因为它似乎不像 node-mysql 那样活跃(尽管这可能只是意味着它已经完成)。

这是一段用户注册代码,它使用了sanitizer模块,以及 node-mysql 准备好的语句式语法(正如我上面提到的,它会进行字符转义),分别防止跨站点脚本和 sql 注入:

// 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?
   });
4

6 回答 6

64

node-mysql库在您已经在使用时自动执行转义。见https://github.com/felixge/node-mysql#escaping-query-values

于 2013-04-03T04:24:19.267 回答
14

该库在自述文件中有一个关于转义的部分。它是 Javascript-native,所以我不建议切换到node-mysql-native。该文档说明了这些转义准则:

编辑: node-mysql-native也是一个纯 Javascript 解决方案。

  • 数字保持不变
  • 布尔值转换为true/false字符串
  • 日期对象转换为YYYY-mm-dd HH:ii:ss字符串
  • 缓冲区被转换为十六进制字符串,例如X'0fa5'
  • 字符串被安全转义
  • 数组变成列表,例如['a', 'b']变成'a', 'b'
  • 嵌套数组变成分组列表(用于批量插入),例如[['a', 'b'], ['c', 'd']]变成('a', 'b'), ('c', 'd')
  • 对象变成key = 'val'对。嵌套对象被转换为字符串。
  • undefined/null转换为NULL
  • NaN/Infinity保持原样。MySQL 不支持这些,并且尝试将它们作为值插入将触发 MySQL 错误,直到它们实现支持。

这允许您执行以下操作:

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'
});

还有这个:

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'
});

除了这些函数,您还可以使用转义函数:

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

要转义查询标识符:

mysql.escapeId(identifier);

作为对您对准备好的陈述的评论的回应:

从可用性的角度来看,这个模块很棒,但它还没有实现类似于 PHP 的 Prepared Statements 的东西。

准备好的语句在此连接器的待办事项列表中,但此模块至少允许您指定与准备好的语句非常相似的自定义格式。这是自述文件中的一个示例:

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));
};

这会更改连接的查询格式,因此您可以使用如下查询:

connection.query("UPDATE posts SET title = :title", { title: "Hello MySQL" });
//equivalent to
connection.query("UPDATE posts SET title = " + mysql.escape("Hello MySQL");
于 2013-04-03T05:50:24.907 回答
12

关于测试您正在使用的模块是否安全,您可以采取多种途径。我将讨论每种方法的优缺点,以便您做出更明智的决定。

目前,您正在使用的模块没有任何漏洞,但是,这通常会导致错误的安全感,因为目前很可能存在漏洞正在利用您正在使用的模块/软件包,而您不会在供应商应用修复程序/补丁之前收到问题警报。

  1. 为了及时了解漏洞,您需要关注邮件列表、论坛、IRC 和其他与黑客相关的讨论。专业人士:您经常会在供应商收到警报或发布修复/补丁以修复对其软件的潜在攻击途径之前意识到库中的潜在问题。CON:这可能非常耗时且占用大量资源。如果您确实使用 RSS 提要、日志解析(IRC 聊天日志)和/或使用关键短语(在本例中为 node-mysql-native)和通知的网络爬虫程序,可以帮助减少花费在这些资源上的时间。

  2. 创建一个 fuzzer,使用一个fuzzer或其他漏洞框架,如metasploitsqlMap等来帮助测试供应商可能没有寻找的问题。PRO:这可以证明是确保您正在实施的模块/软件对于公众访问是安全的达到可接受水平的可靠方法。CON:这也变得耗时且昂贵。另一个问题将源于误报以及对存在问题但未被注意到的结果的未受过教育的审查。

真正的安全性和一般的应用程序安全性可能非常耗时且资源密集。管理人员将始终使用的一件事是确定执行上述两个选项的成本效益(人力、资源、时间、薪酬等)的公式。

无论如何,我意识到这不是可能一直希望的“是”或“否”答案,但我认为在他们对相关软件进行分析之前,任何人都不能给你这个答案。

于 2013-12-14T13:34:21.557 回答
5

Mysql-native 已经过时,所以它变成了 MySQL2,这是一个在原始 MySQL 模块团队的帮助下创建的新模块。这个模块有更多的特性,我认为它有你想要的,因为它已经准备了语句(通过 using.execute()),就像在 PHP 中一样,以提高安全性。

它也非常活跃(最后一次更改是 2-1 天)我之前没有尝试过,但我认为这是你想要的,还有更多。

于 2018-08-24T18:53:34.777 回答
1

防止 SQL 注入

SQL 注入是一种常见的网络黑客技术,用于破坏或滥用您的数据库。为防止 SQL 注入,当查询值是用户提供的变量时,应使用转义值。

使用 mysql.escape() 方法转义查询值:

var adr = 'Mountain 21';
var sql = 'SELECT * FROM customers WHERE address = ' + mysql.escape(adr);
con.query(sql, function (err, result) {
  if (err) throw err;
  console.log(result);
});

使用占位符转义查询值?方法:

var adr = 'Mountain 21';
var sql = 'SELECT * FROM customers WHERE address = ?';
con.query(sql, [adr], function (err, result) {
  if (err) throw err;
  console.log(result);
});

更多详情

于 2021-11-07T16:11:13.673 回答
-1

最简单的方法是在导出到路由的自己的模块中处理所有数据库交互。如果您的路线没有数据库的上下文,那么 SQL 无论如何都无法触及它。

于 2018-06-15T22:59:28.313 回答