【发布时间】:2021-05-01 23:45:02
【问题描述】:
我一直在使用带有 express 和 ejs 模板的 node.js 开发一个“员工休假管理”网络应用程序项目供我们内部使用。现在,我的雇主想让我通过互联网访问该应用程序,我担心 SQL 注入。
假设我在 html 中有一个这样的按钮:
<a href="/edit/<%= ReqID %>">Edit</a>
这将来自index.js 文件的GET:
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]
})
})
}
}
那里可能有两个或更多查询,我正在使用带有 multipleStatements: true 的 node.js 驱动程序 mysql 并且我知道警告 "Support for multiple statements is disabled for security reasons (it allows for SQL injection attacks if values are not properly escaped)." 这将在浏览器地址框中返回类似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 tble 中删除数据,并且确保第二个drop table 命令也有效。第一次尝试后,我使用相同的 sql 注入语法刷新浏览器,并收到以下消息:
{"code":"ER_BAD_TABLE_ERROR","errno":1051,"sqlMessage":"未知表 'users'","sqlState":"42S02","index":1,"sql":"SELECT * FROM table1 WHERE ReqID="ReqID;";drop table users;";SELECT * FROM table1 WHERE ReqID="ReqID;";drop table users;";"}
所以,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;(中间单引号)等,它似乎不再放弃表格了。但是,我仍然看到该语句被发送到服务器,所以我仍然对 DROP 语法在某种程度上有效。
更新 2:
注意:幸运的是,部署已经延迟,我有更多时间来解决问题。
研究了一段时间,考虑到cmets并测试了多种方法,我想出了这个结构:
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
});
});
});
}
这很简单,对我当前的代码没有重大改变。这是否足以防止 node.js 中的 SQL 注入?
【问题讨论】:
-
您可以将
ReqID解析为数值(假设它是数字)。我也不明白你为什么不提出 2 个请求,或者使用外部联接来获取数据?不知道您请求什么数据真的很难说 -
我看不出你的第二个例子和第三个例子有什么区别。他们不一样吗?
-
至于多个语句,我真的不明白什么时候会有用。为什么不单独执行每个语句?启用多个语句有什么好处?
-
我还建议使用github.com/sidorares/node-mysql2,因为它支持原生准备好的语句。
-
如果您想使用
multipleStatements: true,请确保清理您的输入。使用joi模块清理您的输入
标签: mysql node.js sql-injection