【问题标题】:Is using org.postgresql.core.Utils.escapeLiteral enough to prevent SQL Injections?使用 org.postgresql.core.Utils.escapeLiteral 是否足以防止 SQL 注入?
【发布时间】:2017-09-24 12:55:23
【问题描述】:

在构建 sql 查询和更新以提交到我的数据库之前,我需要清理一些用户输入的数据。

我知道最好使用prepared statements,但这不是一个选项。不幸的是,我被 escaping all user supplied Input 卡住了。

看起来 Postgres JDBC 库带有一个进行字符串转义的工具。见org.postgresql.core.Utils.escapeLiteral(..)(附在下面)。我希望既然 Postgres 附带了它,那么它可以安全使用。经过几个小时的谷歌搜索和查看 SQL 备忘单后,我无法找到一个可以打破这一点的示例。

以下看起来足够安全吗?

public class FruitDb {

    private Connection connection;

    public void findFruit ( String /* user enterable field */ fruitColor ) {

        String query = "SELECT * FROM fruit WHERE fruit_color = " + quote( fruitColor );

        Statement statement = connection.createStatement();
        statement.executeQuery( sql );
    }

    private String quote( String toQuote ) {
        return "'" + Utils.escapeLiteral( null, s, true ).toString() + "'";
    }

}

对于那些感兴趣的人,这里是Utils.escapeLiteral 的实现。对我来说看起来相当安全......

package org.postgresql.core;
class Utils { 

    ... 

    /**
     * Escape the given literal <tt>value</tt> and append it to the string builder
     * <tt>sbuf</tt>. If <tt>sbuf</tt> is <tt>null</tt>, a new StringBuilder will be
     * returned. The argument <tt>standardConformingStrings</tt> defines whether the
     * backend expects standard-conforming string literals or allows backslash
     * escape sequences.
     * 
     * @param sbuf the string builder to append to; or <tt>null</tt>
     * @param value the string value
     * @param standardConformingStrings if standard conforming strings should be used
     * @return the sbuf argument; or a new string builder for sbuf == null
     * @throws SQLException if the string contains a <tt>\0</tt> character
     */
    public static StringBuilder escapeLiteral(StringBuilder sbuf, String value, boolean standardConformingStrings)
        throws SQLException
    {
        if (sbuf == null)
        {
            sbuf = new StringBuilder(value.length() * 11 / 10); // Add 10% for escaping.
        }
        doAppendEscapedLiteral(sbuf, value, standardConformingStrings);
        return sbuf;
    }


    private static void doAppendEscapedLiteral(Appendable sbuf, String value, boolean standardConformingStrings)
        throws SQLException
    {
        try
        {
            if (standardConformingStrings)
            {
                // With standard_conforming_strings on, escape only single-quotes.
                for (int i = 0; i < value.length(); ++i)
                {
                    char ch = value.charAt(i);
                    if (ch == '\0')
                        throw new PSQLException(GT.tr("Zero bytes may not occur in string parameters."), PSQLState.INVALID_PARAMETER_VALUE);
                    if (ch == '\'')
                        sbuf.append('\'');
                    sbuf.append(ch);
                }
            }
            else
            {
                 // REMOVED.  I am using standard encoding. 
            }
        }
        catch (IOException e)
        {
            throw new PSQLException(GT.tr("No IOException expected from StringBuffer or StringBuilder"), PSQLState.UNEXPECTED_ERROR, e);
        }
    }
}

类似问题:

【问题讨论】:

  • 为什么没有 PreparedStatements 选项。这是防止 SQL 注入的唯一 100% 安全方法。
  • @a_horse_with_no_name - 两个原因 1. 我试图了解问题,但无法说服自己这是一个问题。 2.遗留代码。很多。
  • 如果你暗示你正在重构使用Utils.escapeLiteral,你为什么不重构使用准备好的语句呢?除非现有代码已经使用Utils.escapeLiteral?
  • 如果所有遗留代码都遵循类似的模式......使用正则表达式将您的示例转换为准备好的语句将非常简单。我之前已经为修改数百条类似的代码做过类似的更改……没有什么能阻止您编写匹配并一次性替换多行的正则表达式。如果代码不是很一致,显然会变得更加困难。
  • JFI: COMMENT ON TABLE .. IS 'comment' is not possible with JDBC PreparedStatement.. 需要某种形式的转义

标签: java postgresql sql-injection


【解决方案1】:

是的,使用 escapeLiteral() 和 escapeIdentifier() 是安全的。

实际上,escapeLiteral() 和 escapeIdentifier() 是 libpq 中定义的 PQescapeLiteral() 和 PQescapeIdentifier() 的补充。

主要区别在于 JDBC 的 escapeLiteral() 不考虑数据库连接来验证字符编码。但是,Java 的内部编码是 Unicode 并转换为数据库字符编码。所以,没关系。

我注意到的另一个区别是如何将字符串转义为 SQL Literal。 PQescapeLiteral 为字符串添加引号,但 escapeLiteral() 没有。例如abc'xyz 使用 PQescapeLiteral() 变成了 'abc''xyz',但它变成了 abc''xyz(注意结果周围没有引号)

escapeIdentifier() 像 PQescapeIdentifier() 一样添加引号。例如abc"xyz 变成了"abc""xyz"

CERT TOP 10 安全编码实践 #7(清理输出)建议清理所有变量,无论之前的检查/验证如何。可以通过使用准备好的查询来避免使用 escapeLiteral(),但不能使用 escapeIdentifier(),因为准备好的查询不能分离参数化标识符。即

SELECT user_specified_col FROM tbl;

SELECT * FROM tbl ORDER BY user_specified_col;

开发人员必须严格验证“user_specified_col”,但他们必须清理参数以防验证不当。即输出清理必须独立完成。

【讨论】:

  • 当前的 escapeLiteral() 行为根本没有意义。我想它将来可能会改变。我建议制作自己的包装器,使其像 PQescapeLiteral() 一样工作。
猜你喜欢
  • 2016-02-19
  • 1970-01-01
  • 2010-10-28
  • 2011-10-16
  • 2011-10-25
  • 1970-01-01
  • 2012-07-30
  • 1970-01-01
  • 2021-10-06
相关资源
最近更新 更多