【问题标题】:java CallableStatement vs SELECT regards to SQL injectionjava CallableStatement vs SELECT 关于 SQL 注入
【发布时间】:2016-04-26 08:23:36
【问题描述】:

我希望能更好地理解 CallableStatement。 所以情况是我的 postgre 数据库中有一个存储函数。

从参数是用户输入(HTTP POST 参数)的 java 调用这些函数我不得不担心 SQL 注入。

所以在 java 中这样做:

String statementSQL = "SELECT my_function(" + toPostgresString(person.getEmail()) + "," + toPostgresString(person.getPhone()) + "," + toPostgresString(person.getDealType())+ ");"; 

在哪里

private static String toPostgresString(String value) {
    if (value == null || value.equals("")) {
        return POSTGRES.NULL;
    } else {
        return "'" + value + "'";
    }
}

后一种方法确保所有参数都被视为字符串,但我读到当参数是恶意的时,这种解决方案仍然很危险。

所以我尝试使用 CallableStatements,因为据说它们更安全。

CallableStatement statement = connection.prepareCall(" { call my_function( ?, ?, ? ) } ");

statement.setString(1, person.email());
statement.setString(2, person.phone());
statement.setString(3, person.getDealType());
statement.execute();
statement.close();

问题: 在 SELECT 和 CallableStatement 两种情况下,参数都被发送到数据库函数,并在那里被视为函数本身定义的数据类型。 CallableStatement 仅在数据类型不匹配时(在声明的函数和 statement.set() 中)失败。

当在这两种情况下,参数都被视为字符串(但在 '' 之间)时,CallableStatement 与 SELECT 相比如何更安全。

谢谢。

【问题讨论】:

    标签: java sql-server postgresql sql-injection


    【解决方案1】:

    PreparedStatementCallableStatement 都提供了 setXXX 方法来设置参数。他们实际上并没有清理参数,他们只是在线上为它们使用二进制类型,所以“在这两种情况下,参数都被视为字符串”部分是错误的。

    PreparedStatement 还提供了一些较小的性能优势,因为驱动程序实际上会在查询被调用足够多次后创建服务器端准备好的语句。

    除非绝对必要,否则您永远不应该使用普通的 Statement,并且始终使用 setXXX 方法,因为这就是它们的用途。

    【讨论】:

    • 等等,但是我应该如何清理参数?那么 Callable 和 Prepared 是否仅用于性能?有人说做消毒,有人说不消毒。他们提供什么安全性呢?谢谢
    • 您不需要对它们进行消毒。我只是说驱动程序不会将参数连接在一起。它以二进制格式发送它们,所以你不能在那里注入 SQL。
    • 对,但您也可以将恶意参数作为二进制文件发送?然后它们会在数据库中变成字符串并且仍然很危险?我不太了解 JDBC 的内部工作原理。
    • 没有。因为它们不会被解释为查询的一部分。因此,如果参数包含',则不会被解释为字符串的结尾。任何地方都没有字符串连接。
    【解决方案2】:

    据我所知,CallableStatement.setXXX() 会为您清理参数。所以它不仅仅是简单的字符串连接。

    另一方面,当你连接 String 时,你可能会遇到讨厌的攻击,比如停止前一个语句,然后开始一个新语句:`';删除表用户; --

    xkcd上查看更多信息。

    【讨论】:

    • 所以使用 CallableStatement.setXXX() 我不需要担心为输入创建单独的白名单?问题是,除了这个人之外,我还有一大堆地址组件,其中为地址部分创建白名单非常困难,因为其中可能有很多不同的字符(数字、-、:、字符串等)。
    • 恐怕您仍然需要根据业务需求验证您的输入。 setXXX() 方法为您所做的只是保护您免受 sql 注入攻击。
    • 是的,这对我来说很好。进行 API 调用的唯一合法方式是来自指定的客户端,该客户端将自动设置地址组件。我想保护数据库免受来自非法来源的 API 调用的影响,因此它们可以被我的服务器拒绝。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-01
    • 1970-01-01
    • 2013-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多