【问题标题】:How do Prepared Statements prevent SQL injection better than Statements?Prepared Statements 如何比 Statements 更好地防止 SQL 注入?
【发布时间】:2014-04-14 04:25:49
【问题描述】:

背景:我已经开始了一个项目,使用JDBC和MYSQL来模拟一个书店,都是本地的。为了连接到数据库,我开始使用语句,但我开始读到,当多次使用仅更改其参数的查询时,对这些查询使用 PreparedStatement 会更有效。然而,我读到最多的优点是 PreparedStatements 如何更好地防止 SQL 注入。

来源: 此线程的答案here
谷歌
教授

我的问题: 在处理参数化查询时,PreparedStatements 如何比 Statements 更好地防止 SQL 注入,甚至是不同的?我很困惑,因为如果我理解正确,这些值仍然会传递到执行的 SQL 语句中,这取决于程序员来清理输入。

【问题讨论】:

  • "由程序员来清理输入。"那么可能出了什么问题呢?
  • 它们更安全,因为它们使用参数。从代码或用户连接字符串的准备好的语句将完全没有意义,不是吗。每次更改参数时,您都必须重新准备它。
  • 阅读答案的内容并尝试关注那里发布的链接。例如:SQL Injection article from Wikipedia
  • 如何使用参数更安全? @托尼霍普金森
  • 如果您使用 PreparedStatements,为什么不必进行清理? @他的

标签: java mysql sql jdbc prepared-statement


【解决方案1】:

框架,Sql驱动程序确保对输入进行转义。如果您使用字符串语句并正确转义 - 将获得相同的结果。但不建议这样做,因为 Preparend 语句看起来像更多的代码行,但也会导致更多的结构化代码。而不是长长的 sql 行。

另外,因为我们单独设置每个参数并明确地底层驱动程序类可以根据使用的数据库正确地转义它们。这意味着您可以通过配置更改数据库,但无论驱动程序是否负责转义。因此,一个数据库可能需要转义斜线,而另一个可能需要两个单引号 ...

这也可以减少代码,因为您无需为此烦恼。简单地说,你让应用代码下一级的框架/通用类来处理它。

【讨论】:

    【解决方案2】:

    您是对的,您可以自己做所有的卫生工作,从而避免注射。但这更容易出错,因此更不安全。换句话说,自己动手会带来更多可能导致注入漏洞的错误。

    一个问题是转义规则可能因数据库而异。例如,标准 SQL 仅允许使用单引号 ('foo') 中的字符串文字,因此您的卫生设施可能只会避开那些;但是 MySQL 允许使用双引号 ("foo") 中的字符串文字,如果你不清理它们,那么如果你使用 MySQL,就会受到注入攻击。

    如果您使用PreparedStatement,则该接口的实现由相应的 JDBC Driver 提供,该实现负责转义您的输入。这意味着清理代码是由编写 JDBC 驱动程序的人作为一个整体编写的,而这些人大概知道 DB 具体转义规则的来龙去脉。他们也很可能对这些转义规则的测试比您测试手动转义功能更彻底。

    因此,如果您编写 preparedStatement.setString(1, name),则该方法的实现(同样,由 JDBC 驱动程序人员为您正在使用的数据库编写)可能大致如下:

    public void setString(int idx, String value) {
        String sanitized = ourPrivateSanitizeMethod(value);
        internalSetString(idx, value);
    }
    

    (请记住,上面的代码是一个非常粗略的草图;许多 JDBC 驱动程序实际上处理它的方式大不相同,但原理基本相同。)

    另一个问题是myUserInputVar 是否已被清理可能并不明显。取下面的sn-p:

    private void updateUser(int name, String id) throws SQLException {
        myStat.executeUpdate("UPDATE user SET name=" + name + " WHERE id=" + id);
    }
    

    这样安全吗?您不知道,因为代码中没有任何内容表明 name 是否经过消毒。而且你不能只是重新清理“为了安全起见”,因为这会改变输入(例如,hello ' world 会变成hello '' world)。另一方面,UPDATE user SET name=? WHERE id=? 的准备好的语句总是安全的,因为PreparedStatement 的实现在将值插入? 之前会转义输入。

    【讨论】:

    • 你说自己做,但还有谁来做呢?您提供的剪辑也可以与这样的准备好的语句一起使用:private void executePrepStat(PID_ADDUSER, String name, int id) { prepStat[ PID_ADDUSER ].setString(1, name); prepStat[ PID_ADDUSER ].setInt(2, id); } 您知道该名称或 ID 是否安全吗?不,那有什么区别?
    • executePrepStat 为您完成!
    • 具体来说,prepStat.setString 会为您完成。
    • 其实大部分驱动都不会转义参数:参数通常和语句分开发送,因此不需要转义。
    【解决方案3】:

    当使用PreparedStatement 的本意使用方式时 - 使用带有参数占位符的固定查询文本,没有外部值的串联 - 然后您就可以防止 SQL 注入。

    这种保护的工作方式大致有两种:

    1. JDBC 驱动程序正确地转义值并将它们插入到占位符位置的查询中,并将完成的查询发送到服务器(AFAIK 只有 MySQL 连接器/J 这样做,并且只有 useServerPrepStmts=false 是默认)。

    2. JDBC 驱动程序将查询文本(带有占位符)发送到服务器,服务器准备查询并返回参数描述(例如类型和长度)。然后,JDBC 驱动程序收集参数值并将它们作为参数值块发送到服务器。然后服务器使用这些参数值执行准备好的查询。

    鉴于服务器准备和执行查询的方式,此时不能发生 SQL 注入(当然,除非您执行存储过程,并且该存储过程通过连接动态创建查询)。

    【讨论】:

      猜你喜欢
      • 2011-11-28
      • 2016-02-19
      • 2016-10-25
      • 2013-05-05
      • 1970-01-01
      • 2012-08-23
      • 2013-08-25
      • 2012-11-20
      • 2015-09-03
      相关资源
      最近更新 更多