在 Node.js 中防止 SQL 注入

有没有可能像 PHP 使用预备语句那样防止 Node.js 中的 SQL 注入(最好使用模块)。

如果是这样,如何? 如果不是,有什么例子可能会绕过我提供的代码(见下文)。


一些背景:

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

根据我的理解,PHP 实现了预处理语句,其中包括 帮了大忙在防止 SQL 注入中的作用。但是,我担心我的 node.js 应用程序可能会受到类似的攻击 即使使用默认提供的字符串转义(如下面的代码片段所示)。

Node-mysql 似乎是 node.js 最流行的 mysql 连接器,所以我想知道其他人可能会做些什么(如果有的话)来解释这个问题——或者这是否是 node.js 的一个问题(不确定这怎么会不是,因为涉及用户/客户端输入)。

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

下面是一段用户注册代码,它使用了 消毒剂模块,以及 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?
});
190834 次浏览

The node-mysql library automatically performs escaping when used as you are already doing. See https://github.com/felixge/node-mysql#escaping-query-values

The library has a section in the readme about escaping. It's Javascript-native, so I do not suggest switching to node-mysql-native. The documentation states these guidelines for escaping:

Edit: node-mysql-native is also a pure-Javascript solution.

  • Numbers are left untouched
  • Booleans are converted to true / false strings
  • Date objects are converted to YYYY-mm-dd HH:ii:ss strings
  • Buffers are converted to hex strings, e.g. X'0fa5'
  • Strings are safely escaped
  • Arrays are turned into list, e.g. ['a', 'b'] turns into 'a', 'b'
  • Nested arrays are turned into grouped lists (for bulk inserts), e.g. [['a', 'b'], ['c', 'd']] turns into ('a', 'b'), ('c', 'd')
  • Objects are turned into key = 'val' pairs. Nested objects are cast to strings.
  • undefined / null are converted to NULL
  • NaN / Infinity are left as-is. MySQL does not support these, and trying to insert them as values will trigger MySQL errors until they implement support.

This allows for you to do things like so:

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

As well as this:

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

Aside from those functions, you can also use the escape functions:

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

To escape query identifiers:

mysql.escapeId(identifier);

And as a response to your comment on prepared statements:

From a usability perspective, the module is great, but it has not yet implemented something akin to PHP's Prepared Statements.

The prepared statements are on the todo list for this connector, but this module at least allows you to specify custom formats that can be very similar to prepared statements. Here's an example from the readme:

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

This changes the query format of the connection so you can use queries like this:

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

In regards to testing if a module you are utilizing is secure or not there are several routes you can take. I will touch on the pros/cons of each so you can make a more informed decision.

Currently, there aren't any vulnerabilities for the module you are utilizing, however, this can often lead to a false sense of security as there very well could be a vulnerability currently exploiting the module/software package you are using and you wouldn't be alerted to a problem until the vendor applies a fix/patch.

  1. To keep abreast of vulnerabilities you will need to follow mailing lists, forums, IRC & other hacking related discussions. PRO: You can often times you will become aware of potential problems within a library before a vendor has been alerted or has issued a fix/patch to remedy the potential avenue of attack on their software. CON: This can be very time consuming and resource intensive. If you do go this route a bot using RSS feeds, log parsing (IRC chat logs) and or a web scraper using key phrases (in this case node-mysql-native) and notifications can help reduce time spent trolling these resources.

  2. Create a fuzzer, use a fuzzer or other vulnerability framework such as metasploit, sqlMap etc. to help test for problems that the vendor may not have looked for. PRO: This can prove to be a sure fire method of ensuring to an acceptable level whether or not the module/software you are implementing is safe for public access. CON: This also becomes time consuming and costly. The other problem will stem from false positives as well as uneducated review of the results where a problem resides but is not noticed.

Really security, and application security in general can be very time consuming and resource intensive. One thing managers will always use is a formula to determine the cost effectiveness (manpower, resources, time, pay etc) of performing the above two options.

Anyways, I realize this is not a 'yes' or 'no' answer that may have been hoping for but I don't think anyone can give that to you until they perform an analysis of the software in question.

Mysql-native has been outdated so it became MySQL2 that is a new module created with the help of the original MySQL module's team. This module has more features and I think it has what you want as it has prepared statements(by using.execute()) like in PHP for more security.

It's also very active(the last change was from 2-1 days) I didn't try it before but I think it's what you want and more.

Preventing SQL injections

SQL injections is a common web hacking technique to destroy or misuse your database. To prevent SQL injections, you should use escape the values when query values are variables provided by the user.

Escape query values by using the mysql.escape() method:

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

Escape query values by using the placeholder ? method:

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

More Detail