防止 Node.js 中的 SQL 注入

Preventing SQL injection in Node.js

提问人:funseiki 提问时间:4/3/2013 最后编辑:Communityfunseiki 更新时间:11/8/2021 访问量:203220

问:

是否可以像 PHP 具有防止 Node.js 中的 SQL 注入(最好使用模块)一样防止它们。

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


一些背景:

我正在使用 node-mysql 模块制作一个带有由 Node.js + MySql 组成的后端堆栈的 Web 应用程序。从可用性的角度来看,该模块很棒,但它还没有实现类似于 PHP 的预处理语句的东西(尽管我知道它在待办事项上)。

根据我的理解,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?
   });
javascript mysql 节点 .js sql 注入 node-mysql

评论


答:

67赞 Michael Pratt 4/3/2013 #1

该库在您已经执行的操作中使用时会自动执行转义。查看 https://github.com/felixge/node-mysql#escaping-query-valuesnode-mysql

评论

0赞 Yousaf 11/24/2017
注意:这些转义值的方法仅在禁用 NO_BACKSLASH_ESCAPES SQL 模式(这是 MySQL 服务器的默认状态)=> source 是 node-MySQL 本身时才有效。他们也有更清洁、更安全的制备方法。github.com/mysqljs/mysql#preparing-queries
17赞 hexacyanide 4/3/2013 #2

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

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

  • 数字保持不变
  • 布尔值转换为 / 字符串truefalse
  • Date 对象转换为字符串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 的预准备语句的东西。

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

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

评论

0赞 funseiki 4/3/2013
感谢您的回复 - 我知道有准备的风格。然而,在下面,角色正在被逃脱。参见:“但是,它实际上只是使用相同的 connection.escape()”。至于不使用 node-mysql-native:这就是我正在努力解决的问题。如果 node-mysql-native 实现了准备好的语句并且其实现阻止了 SQL 注入,那么在 node-mysql 拥有它们之前,我不应该进行切换吗?
0赞 Andrey Sidorov 4/3/2013
这有点像先有鸡还是先有蛋的问题。我没有积极开发我的驱动程序,因为大多数人都使用 @felixge,我可能会尝试找一些时间将准备好的语句移植到 node-mysql,因为它确实提供了一些性能优势(并且可能使 sql 注入更加困难)。如果您决定试一试,请随时发表评论/发布问题
1赞 hexacyanide 4/3/2013
@funseiki 我确信准备好的语句将是最好的解决方案,但我非常确定转义将阻止 SQL 注入。由于该模块本身由 Joyent 支持,因此该模块处于活动状态,并且显然经过了彻底的检查。如果这个模块还没有准备好投入生产,那么我认为该模块上个月的平均下载量不会达到 1000 次/天。请注意,node-mysql-native 距离上次开发已经过去了 6 个月,而 node-mysql 非常活跃,有多个人在工作。
0赞 funseiki 4/3/2013
@AndreySidorov 感谢您的评论 - 如果我尝试解决它,我会发布更新。不过,我认为这不会很快发生,因为它似乎不是一个容易处理的野兽(需要比我目前有时间更多的研究)。也感谢您制作该驱动程序 - 你们是 Node.js 使应用程序快速快速运行变得容易的原因
0赞 funseiki 4/3/2013
@hexacyanide 由于 node-mysql 非常受欢迎,我希望我能从社区成员那里得到关于他们可能遇到(或阻止)的安全问题的回应,以及一个令人信服的论据,说明为什么当前的字符转义方法对他们的代码来说足够安全。
12赞 jas- 12/14/2013 #3

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

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

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

  2. 创建一个模糊器,使用模糊器或其他漏洞框架,如metasploitsqlMap等,以帮助测试供应商可能没有发现的问题。 优点:这可以证明是一种可靠的方法,可以确保在可接受的水平上,您正在实现的模块/软件是否对公众访问是安全的。 缺点:这也变得耗时且成本高昂。另一个问题将源于误报以及对问题所在但未被注意到的结果的未受过教育的审查。

真正的安全性,以及应用程序安全性通常可能非常耗时且占用大量资源。管理者总是使用的一件事是确定执行上述两种选择的成本效益(人力、资源、时间、薪酬等)的公式。

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

5赞 Boy pro 8/25/2018 #4

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

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

1赞 Khabir 11/8/2021 #5

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

更多详情