使用org.postgresql.core.Utils.escapeLiteral足以阻止SQL注入吗?

时间:2017-04-26 22:45:08

标签: java postgresql sql-injection

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

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

看起来Postgres JDBC库附带了一个用于执行String转义的工具。请参阅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);
        }
    }
}

类似的问题:

1 个答案:

答案 0 :(得分:0)

是的,可以安全地使用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”,但是他们必须清理参数以防不正确的验证。也就是说,输出清理必须独立进行。