使用带有 multipleStatements 的 mysql 驱动程序防止来自 node.js 的 SQL 注入:true
Preventing SQL Injection from node.js using mysql driver with multipleStatements: true
我一直在使用 node.js 以及 express 和 ejs 模板开发供我们内部使用的“员工休假管理”Web 应用程序项目。现在,我的雇主希望我可以通过互联网访问该应用程序,我担心 SQL 注入。
假设我在 html 中有一个这样的按钮:
<a href="/edit/<%= ReqID %>">Edit</a>
这将 GET
来自 index.js
文件:
const { edit } = require("./request");
app.get("/edit/:ReqID", edit);
这将转到 request.js
文件中的模块 edit
:
module.exports = {
edit: (req, res) => {
let ReqID= req.params.ReqID;
let squery = `SELECT * FROM table1 WHERE ReqID="${ReqID}";
SELECT * FROM table2 WHERE ReqID="${ReqID}";`;
db.query(squery, function (err, result) {
if (err) {
return res.status(500).send(err);
}
res.render("edit.ejs", {
srecords1: result[0],
srecords2: result[1]
})
})
}
}
那里可能有两个或更多查询,我使用 mysql
驱动程序 node.js 和 multipleStatements: true
我知道警告 ” Support for multiple statements is disabled for security reasons (it allows for SQL injection attacks if values are not properly escaped)." 这将 return 类似 http://localhost:port/edit/reqid
的内容出现在浏览器地址栏中。我在 youtube 上看到一个视频说 SQL 注入可以通过浏览器的地址框完成,比如 http://localhost:port/edit/reqid;";SELECT * FROM users;
所以我这样做了并且我肯定可以看到该语法被发送到服务器。所以我按照视频中的建议做了这样一个占位符:
module.exports = {
edit: (req, res) => {
let ReqID= req.params.ReqID;
let squery = `SELECT * FROM table1 WHERE ReqID= ?;
SELECT * FROM table2 WHERE ReqID= ?;`;
db.query(squery, [ReqID, ReqID], function (err, result) {
if (err) {
return res.status(500).send(err);
}
res.render("edit.ejs", {
srecords1: result[0],
srecords2: result[1]
})
})
}
}
然后我分别尝试极端的 http://localhost:port/edit/reqid;";DELETE FROM users;
和 http://localhost:port/edit/reqid;";DROP TABLE users;
并且有效!首先它从 users
表中删除数据,并且确定第二个 drop table 命令也有效。第一次尝试后,我用相同的 sql 注入语法刷新浏览器,我收到了这条消息:
{"code":"ER_BAD_TABLE_ERROR","errno":1051,"sqlMessage":"Unknown table 'users'","sqlState":"42S02","index":1,"sql":"SELECT * FROM table1 WHERE ReqID= "ReqID;";drop table users;";SELECT * FROM table1 WHERE ReqID= "ReqID;";drop table users;";"}
因此,table users
显然已从数据库中删除。
更新:
我根据获得的信息做了进一步的测试 from this answer 我做了这样的事情:
module.exports = {
edit: (req, res) => {
let ReqID= req.params.ReqID;
db.query(`SELECT * FROM table1 WHERE ReqID= ?; SELECT * FROM table2 WHERE ReqID= ?;` , [ReqID, ReqID], function (err, result) {
if (err) {
return res.status(500).send(err);
}
res.render("edit.ejs", {
srecords1: result[0],
srecords2: result[1]
})
})
}
}
然后我用 http://localhost:port/edit/reqid;";DROP TABLE users;
的多个变体重新测试(中间有双引号)
http://localhost:port/edit/reqid;';DROP TABLE users;
(中间的单引号)等等,它似乎不再删除 table 了。但是,我仍然看到语句被发送到服务器,所以我仍然担心 DROP
语法以某种方式有效。
更新二:
注意:幸运的是,部署延迟了,我有更多时间来解决问题。
经过一段时间的研究,考虑到评论并测试了多种方法,我想出了这个结构:
function(req, res) {
let dcode = [req.body.dcode];
let query1 =`SELECT col1, col2 FROM table1 WHERE DCode=?`;
db.query(query1, dcode, function(err, result_1) {
if (err) {
return res.status(500).send(err);
}
let query2 =`SELECT col1, col2 FROM table2 WHERE DCode=?`;
db.query(query2, dcode, function(err, result_2) {
if (err) {
return res.status(500).send(err);
}
res.render("login.ejs", {
result1: result_1,
result2: result_2
});
});
});
}
这很简单,对我当前的代码没有太大的改变。这足以防止 SQL 在 node.js 中注入吗?
允许多语句字符串本身会引发 SQL 注入。所以,避免它。
方案A:
考虑结束一个数组(可能在JSON)到服务器;然后让它执行每个语句,然后 return 一个结果集数组。
但是一次只发布一个语句会更简单。
(如果客户端和服务端距离较远,一次一个语句的方法可能会导致明显的延迟。)
B计划:
为任何多语句需求构建合适的存储过程。这在可行的情况下避免了多语句调用。并避免延迟问题(通常)。
以下是一些可能有用的建议:
- 永远不要使用这样的模板字符串:
Select * from table where id = ${value}
。 SQL 注射将会发生 - 100%!。相反,您应该使用内置的驱动程序防御机制。像这样:query('Select * from table where id = ?', [value])
。这应该可以防止 SQL 注入。
- 每个查询使用单个语句。如果您需要在对数据库的一次请求中执行多项操作 - 考虑创建 stored procedure。存储过程也有内置的安全机制。
- 考虑使用 query builder or ORM。他们在内置驱动程序一的基础上还有额外的安全层。
- 您还可以在 3rd party library.
的帮助下显式转义 SQL 字符串
我一直在使用 node.js 以及 express 和 ejs 模板开发供我们内部使用的“员工休假管理”Web 应用程序项目。现在,我的雇主希望我可以通过互联网访问该应用程序,我担心 SQL 注入。
假设我在 html 中有一个这样的按钮:
<a href="/edit/<%= ReqID %>">Edit</a>
这将 GET
来自 index.js
文件:
const { edit } = require("./request");
app.get("/edit/:ReqID", edit);
这将转到 request.js
文件中的模块 edit
:
module.exports = {
edit: (req, res) => {
let ReqID= req.params.ReqID;
let squery = `SELECT * FROM table1 WHERE ReqID="${ReqID}";
SELECT * FROM table2 WHERE ReqID="${ReqID}";`;
db.query(squery, function (err, result) {
if (err) {
return res.status(500).send(err);
}
res.render("edit.ejs", {
srecords1: result[0],
srecords2: result[1]
})
})
}
}
那里可能有两个或更多查询,我使用 mysql
驱动程序 node.js 和 multipleStatements: true
我知道警告 ” Support for multiple statements is disabled for security reasons (it allows for SQL injection attacks if values are not properly escaped)." 这将 return 类似 http://localhost:port/edit/reqid
的内容出现在浏览器地址栏中。我在 youtube 上看到一个视频说 SQL 注入可以通过浏览器的地址框完成,比如 http://localhost:port/edit/reqid;";SELECT * FROM users;
所以我这样做了并且我肯定可以看到该语法被发送到服务器。所以我按照视频中的建议做了这样一个占位符:
module.exports = {
edit: (req, res) => {
let ReqID= req.params.ReqID;
let squery = `SELECT * FROM table1 WHERE ReqID= ?;
SELECT * FROM table2 WHERE ReqID= ?;`;
db.query(squery, [ReqID, ReqID], function (err, result) {
if (err) {
return res.status(500).send(err);
}
res.render("edit.ejs", {
srecords1: result[0],
srecords2: result[1]
})
})
}
}
然后我分别尝试极端的 http://localhost:port/edit/reqid;";DELETE FROM users;
和 http://localhost:port/edit/reqid;";DROP TABLE users;
并且有效!首先它从 users
表中删除数据,并且确定第二个 drop table 命令也有效。第一次尝试后,我用相同的 sql 注入语法刷新浏览器,我收到了这条消息:
{"code":"ER_BAD_TABLE_ERROR","errno":1051,"sqlMessage":"Unknown table 'users'","sqlState":"42S02","index":1,"sql":"SELECT * FROM table1 WHERE ReqID= "ReqID;";drop table users;";SELECT * FROM table1 WHERE ReqID= "ReqID;";drop table users;";"}
因此,table users
显然已从数据库中删除。
更新:
我根据获得的信息做了进一步的测试 from this answer 我做了这样的事情:
module.exports = {
edit: (req, res) => {
let ReqID= req.params.ReqID;
db.query(`SELECT * FROM table1 WHERE ReqID= ?; SELECT * FROM table2 WHERE ReqID= ?;` , [ReqID, ReqID], function (err, result) {
if (err) {
return res.status(500).send(err);
}
res.render("edit.ejs", {
srecords1: result[0],
srecords2: result[1]
})
})
}
}
然后我用 http://localhost:port/edit/reqid;";DROP TABLE users;
的多个变体重新测试(中间有双引号)
http://localhost:port/edit/reqid;';DROP TABLE users;
(中间的单引号)等等,它似乎不再删除 table 了。但是,我仍然看到语句被发送到服务器,所以我仍然担心 DROP
语法以某种方式有效。
更新二:
注意:幸运的是,部署延迟了,我有更多时间来解决问题。
经过一段时间的研究,考虑到评论并测试了多种方法,我想出了这个结构:
function(req, res) {
let dcode = [req.body.dcode];
let query1 =`SELECT col1, col2 FROM table1 WHERE DCode=?`;
db.query(query1, dcode, function(err, result_1) {
if (err) {
return res.status(500).send(err);
}
let query2 =`SELECT col1, col2 FROM table2 WHERE DCode=?`;
db.query(query2, dcode, function(err, result_2) {
if (err) {
return res.status(500).send(err);
}
res.render("login.ejs", {
result1: result_1,
result2: result_2
});
});
});
}
这很简单,对我当前的代码没有太大的改变。这足以防止 SQL 在 node.js 中注入吗?
允许多语句字符串本身会引发 SQL 注入。所以,避免它。
方案A:
考虑结束一个数组(可能在JSON)到服务器;然后让它执行每个语句,然后 return 一个结果集数组。
但是一次只发布一个语句会更简单。
(如果客户端和服务端距离较远,一次一个语句的方法可能会导致明显的延迟。)
B计划:
为任何多语句需求构建合适的存储过程。这在可行的情况下避免了多语句调用。并避免延迟问题(通常)。
以下是一些可能有用的建议:
- 永远不要使用这样的模板字符串:
Select * from table where id = ${value}
。 SQL 注射将会发生 - 100%!。相反,您应该使用内置的驱动程序防御机制。像这样:query('Select * from table where id = ?', [value])
。这应该可以防止 SQL 注入。 - 每个查询使用单个语句。如果您需要在对数据库的一次请求中执行多项操作 - 考虑创建 stored procedure。存储过程也有内置的安全机制。
- 考虑使用 query builder or ORM。他们在内置驱动程序一的基础上还有额外的安全层。
- 您还可以在 3rd party library. 的帮助下显式转义 SQL 字符串