从 commons.lang 迁移 StringEscapeUtils.escapeSql

Posted

技术标签:

【中文标题】从 commons.lang 迁移 StringEscapeUtils.escapeSql【英文标题】:Migrating StringEscapeUtils.escapeSql from commons.lang 【发布时间】:2015-08-19 13:16:18 【问题描述】:

我已经开始将 commons.lang 2 迁移到 commons.lang3。

根据 https://commons.apache.org/proper/commons-lang/article3_0.html

StringEscapeUtils.escapeSql

这是一种误导性的方法,只处理最简单的 SQL 情况。 >由于 SQL 不是 Lang 的重点,所以维护这种方法没有意义。

了解它,但建议使用什么来代替它?

澄清

你能推荐一个第三方来执行类似于StringEscapeUtils.escapeSql的简单escapeSql吗?

【问题讨论】:

描述您的用例。最简单的一种是“运行数据库查询”,为此您通常不需要转义任何 SQL(您可以并且应该使用绑定变量)。 您为什么要这样做?这似乎是个坏主意,所以我可以理解为什么要删除该方法。 简单用例...如果您碰巧在“order by”子句中需要参数,PreparedStatement 将不起作用。 @NathanCrause:嗯。那是什么关系型数据库?可以通过将查询包装到内联视图/子选择中来解决它,以便要排序的表达式成为“列”吗?可能值得为这个特定示例提出一个新问题。 @Thilo 这是一个我没有考虑过的有趣想法。从纯粹的技术角度来看,这似乎是在修补一些不需要修补的东西。要回答第一个问题,那就是 MariaDB。 (这不是自愿的,顺便说一句,我想要 PostgreSQL)。 【参考方案1】:

From the Javadocs:

目前这种方法只能把单引号变成双引号("McHale's Navy" => "McHale's Navy")。

这是方法代码:

  /**
675         * <p>Escapes the characters in a <code>String</code> to be suitable to pass to
676         * an SQL query.</p>
677         *
678         * <p>For example,
679         * <pre>statement.executeQuery("SELECT * FROM MOVIES WHERE TITLE='" + 
680         *   StringEscapeUtils.escapeSql("McHale's Navy") + 
681         *   "'");</pre>
682         * </p>
683         *
684         * <p>At present, this method only turns single-quotes into doubled single-quotes
685         * (<code>"McHale's Navy"</code> => <code>"McHale''s Navy"</code>). It does not
686         * handle the cases of percent (%) or underscore (_) for use in LIKE clauses.</p>
687         *
688         * see http://www.jguru.com/faq/view.jsp?EID=8881
689         * @param str  the string to escape, may be null
690         * @return a new String, escaped for SQL, <code>null</code> if null string input
691         */
692        public static String escapeSql(String str) 
693            if (str == null) 
694                return null;
695            
696            return StringUtils.replace(str, "'", "''");
697        

因此,您可以通过简单地调用 String#replace 来轻松替换该方法。

但是,删除该方法是有原因的。它真的是半生不熟,我想不出你想要使用它的充分理由。例如,要运行 JDBC 查询,您可以而且应该使用绑定变量,而不是尝试插入和转义字符串文字。

【讨论】:

实际上确实存在一个用例:转义部分无法通过绑定变量进行参数化的 SQL 语句。例如。表名本身:***.com/q/1208442/274677 @MarcusJuniusBrutus:我不会指望那里使用相同的转义规则。如果您在架构对象名称中使用空格或引号之类的东西,您已经在自找麻烦了。 @Thilo 这个用例怎么样:如果你碰巧在“order by”子句中有需要的参数,PreparedStatement 就不起作用。 @NathanCrause 不确定你的意思。如果你想在ORDER BY abc DESC 中有变量列名,那么是的,就像 Marcus 关于表名所说的那样,绑定参数不能用于模式对象名称,只能用于值。 @Thilo 这就是重点——我正在研究的系统有一个“order by”子句,其中包含传入的值(排序顺序基于提供的字符串是否位于开头的评分)或列值的结尾)。所以“我想不出一个好的理由”是一个完全不相关的观点,仅仅因为一个用例不能在某一时刻被理解,并不意味着它没有用例。【参考方案2】:

如果您使用的是 JDBC 连接,请准备一个带有以下参数的语句:

con.prepareStatement("INSERT INTO table1 VALUES (?,?)");
pstmt.setInt(1, 200);
pstmt.setString(2, "Julie");
pstmt.executeUpdate();

您不需要转义使用预准备语句中的函数插入的任何元素。这些会自动转义。

这个问题之前已经回答过: Java - escape string to prevent SQL injection

【讨论】:

但它不适用于所有 SQL 子句。正如我在这里的其他 cmets 中提到的那样,如果您的“order by”中碰巧有参数,它就不起作用。 如果您在需要使用本机查询时使用 JPA/Hibernate,这也适用。请改用准备好的语句。 java Query q = entityManager.createQuery("SELECT * FROM people WHERE name = ?") ; q.setParameter(1, "Julie") 【参考方案3】:

OWASP 有一个名为 ESAPI 的 API 提供了其中一些功能,您可以查看一下。

【讨论】:

这里有一个示例和站点内的链接,它解释了如何使用这个 API 以及对其他方法的讨论。 ESAPI.encoder().encodeForSQL(new OracleCodec(), queryparam); cheatsheetseries.owasp.org/cheatsheets/…

以上是关于从 commons.lang 迁移 StringEscapeUtils.escapeSql的主要内容,如果未能解决你的问题,请参考以下文章

关于Apache Commons-Lang3的使用

StringUtils在commons-lang3和commons-lang中的区别

commons-lang3

Commons Lang 介绍

找不org.apache.commons.lang3.builder.EqualsBuilder和commons-lang下载链接

java.lang.ClassNotFoundException: org.apache.commons.lang.exception.NestableRuntimeException