【问题标题】:Preventing SQL injection in Node.js防止 Node.js 中的 SQL 注入
【发布时间】:2013-03-24 14:25:25
【问题描述】:

是否有可能像 PHP 中的 Prepared Statements 一样防止 SQL 注入(最好使用模块)。

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


一些上下文:

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

据我了解,PHP 对预处理语句的实现,除其他外,helped greatly 用于防止 SQL 注入。不过,我担心我的 node.js 应用程序可能会受到类似的攻击,even with the string escaping provided by default(如下面的代码 sn-p 所示)。

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

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

这是一个用户注册码的 sn-p,它使用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 node.js sql-injection node-mysql


    【解决方案1】:

    node-mysql 库在您使用时会自动执行转义。见https://github.com/felixge/node-mysql#escaping-query-values

    【讨论】:

    • 正如我的帖子中提到的,我知道库会转义字符,但如果我不切换到已实现准备好的语句的库,我更担心安全隐患,即是否存在我目前正在做的事情可能会发生 SQL 注入?
    • 转义字符可防止 SQL 注入。当字符没有转义时会发生注入,恶意用户可以利用它来关闭查询并启动新查询,例如删除表或插入虚假记录。使用转义字符,这是不可能的。 Wikipedia 有一些关于 SQL 注入的附加信息。
    • 但是它会阻止所有的 SQL 注入吗? This answer 建议不要(至少对于 PHP + MySQL),并暗示 PHP 的 Prepared Statements 可以。同样,这是在 PHP 的上下文中。
    • 根据您的链接,这只适用于过时版本的 MySQL。我不知道这种特定的攻击是否适用于 Node,但它看起来与非常特定的 PHP 漏洞有关,所以我的直觉是不会。我并不是说 node-mysql 绝对没有漏洞,但它已经在大量生产环境中使用。如果您仍然担心 SQL 注入,我建议您硬着头皮尝试 MongoDB 之类的东西——如果您不使用 SQL,则无法进行 SQL 注入。
    • 看起来是这样,MongoDB 路由是一个好点——尽管当前的设计很适合关系模式。我会等着看其他人是否对安全漏洞有见解 - 否则,似乎共识是坚持使用 node-mysql
    【解决方案2】:

    图书馆在关于转义的自述文件中有一个section。它是 Javascript 原生的,所以我不建议切换到 node-mysql-native。文档说明了这些转义准则:

    编辑:node-mysql-native 也是纯 JavaScript 解决方案。

    • 数字保持不变
    • 布尔值转换为true / false 字符串
    • 日期对象被转换为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 的 Prepared Statements 的东西。

    准备好的语句在此连接器的todo 列表中,但此模块至少允许您指定与准备好的语句非常相似的自定义格式。这是自述文件中的一个示例:

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

    【讨论】:

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

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

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

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

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

    真正的安全性,一般来说,应用程序安全性可能非常耗时且资源密集。管理人员将始终使用的一件事是确定执行上述两个选项的成本效益(人力、资源、时间、薪酬等)的公式。

    无论如何,我意识到这不是可能一直希望得到的“是”或“否”答案,但我认为在对相关软件进行分析之前,任何人都不能给你这个答案。

    【讨论】:

      【解决方案4】:

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

      它也很活跃(最后一次更改是从 2-1 天)我之前没有尝试过,但我认为这是你想要的,而且还有更多。

      【讨论】:

        【解决方案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);
        });
        

        More Detail

        【讨论】:

          【解决方案6】:

          最简单的方法是在导出到路由的自己的模块中处理所有数据库交互。如果你的路由没有数据库的上下文,那么 SQL 无论如何都无法触及它。

          【讨论】:

          • 这并不能真正回答 OP 关于如何消毒的问题。
          猜你喜欢
          • 1970-01-01
          • 2011-06-12
          • 1970-01-01
          • 2011-04-30
          • 2013-02-28
          • 2011-04-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多