使用带有 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计划:

为任何多语句需求构建合适的存储过程。这在可行的情况下避免了多语句调用。并避免延迟问题(通常)。

以下是一些可能有用的建议:

  1. 永远不要使用这样的模板字符串:Select * from table where id = ${value}。 SQL 注射将会发生 - 100%!。相反,您应该使用内置的驱动程序防御机制。像这样:query('Select * from table where id = ?', [value])。这应该可以防止 SQL 注入。
  2. 每个查询使用单个语句。如果您需要在对数据库的一次请求中执行多项操作 - 考虑创建 stored procedure。存储过程也有内置的安全机制。
  3. 考虑使用 query builder or ORM。他们在内置驱动程序一的基础上还有额外的安全层。
  4. 您还可以在 3rd party library.
  5. 的帮助下显式转义 SQL 字符串