Java 相当于 PHP 的 mysql_real_escape_string()

2022-09-03 12:20:03

有没有一个Java等同于PHP的mysql_real_escape_string()?

这是为了在将 SQL 注入尝试传递给 Statement.execute() 之前对其进行转义。

我知道我可以使用ReadyStatement代替,但让我们假设这些是一次性语句,所以准备它们会导致性能降低。我已经将代码更改为使用ReadkMentStatement,但考虑到现有代码的结构方式,escape()函数将使代码更改更易于查看和维护;我更喜欢易于维护的代码,除非有令人信服的理由说明额外的复杂性。此外,数据库对 PreparedStatement 的处理方式也不同,因此这可能会让我们接触到数据库中以前没有遇到过的错误,在发布到生产环境之前需要进行更多的测试。

Apache StringEscapeUtils escapeSQL() 只转义单引号。

后记:我继承的环境中有很多微妙之处,我在问题中故意避免了这些微妙之处。

需要考虑的两点:

1) 预准备语句不是灵丹妙药,不能提供 100% 的 SQL 注入保护。某些数据库驱动程序使用不安全的字符串串联实例化参数化查询,而不是将查询预编译为二进制形式。此外,如果 SQL 依赖于存储过程,则需要确保存储过程本身不会以不安全的方式生成查询。

2) 大多数预准备语句实现将语句绑定到实例化语句的数据库连接。如果使用数据库连接池,则需要小心地
将预准备语句引用仅用于准备它的连接。某些池化机制确实以透明方式实现此目的。否则,您也可以池化预准备语句,或者(最简单但开销更大)为每个查询创建一个新的预准备语句。


答案 1

据我所知,没有“标准”的方法可以做到这一点。

我强烈建议使用准备好的声明,尽管您目前的担忧。对性能的影响可以忽略不计 - 我们也有类似的情况,每秒有几千个语句 - 其中大多数也是一次性的。

您获得的安全性应该比您甚至还没有见过的性能问题高得多。在我看来,这是一个“不要过早优化”的明显情况。

在任何情况下,如果您以后真的发现您遇到了性能问题,请确保准备好的语句确实是原因,方法是仔细分析,然后寻找替代方法。在那之前,你应该省去试图正确逃脱的麻烦。

这一点更为重要,因为我推断您正在开发某种面向公众的网站 - 内部应用程序很少获得足够的流量来关注性能。


答案 2

以下是一些实现您正在寻找的代码。最初在Vnet Publishing wiki上。

https://web.archive.org/web/20131202082741/http://wiki.vnetpublishing.com/Java_Mysql_Real_Escape_String

/**
  * Mysql Utilities
  *        
  * @author Ralph Ritoch <rritoch@gmail.com>
  * @copyright Ralph Ritoch 2011 ALL RIGHTS RESERVED
  * @link http://www.vnetpublishing.com
  *
  */

 package vnet.java.util;

 public class MySQLUtils {

     /**
      * Escape string to protected against SQL Injection
      *
      * You must add a single quote ' around the result of this function for data,
      * or a backtick ` around table and row identifiers. 
      * If this function returns null than the result should be changed
      * to "NULL" without any quote or backtick.
      *
      * @param link
      * @param str
      * @return
      * @throws Exception 
      */

     public static String mysql_real_escape_string(java.sql.Connection link, String str) 
           throws Exception
     {
         if (str == null) {
             return null;
         }

         if (str.replaceAll("[a-zA-Z0-9_!@#$%^&*()-=+~.;:,\\Q[\\E\\Q]\\E<>{}\\/? ]","").length() < 1) {
             return str;
         }

         String clean_string = str;
         clean_string = clean_string.replaceAll("\\\\", "\\\\\\\\");
         clean_string = clean_string.replaceAll("\\n","\\\\n");
         clean_string = clean_string.replaceAll("\\r", "\\\\r");
         clean_string = clean_string.replaceAll("\\t", "\\\\t");
         clean_string = clean_string.replaceAll("\\00", "\\\\0");
         clean_string = clean_string.replaceAll("'", "\\\\'");
         clean_string = clean_string.replaceAll("\\\"", "\\\\\"");

         if (clean_string.replaceAll("[a-zA-Z0-9_!@#$%^&*()-=+~.;:,\\Q[\\E\\Q]\\E<>{}\\/?\\\\\"' ]"
           ,"").length() < 1) 
         {
             return clean_string;
         }

         java.sql.Statement stmt = link.createStatement();
         String qry = "SELECT QUOTE('"+clean_string+"')";

         stmt.executeQuery(qry);
         java.sql.ResultSet resultSet = stmt.getResultSet();
         resultSet.first();
         String r = resultSet.getString(1);
         return r.substring(1,r.length() - 1);       
     }

     /**
      * Escape data to protected against SQL Injection
      *
      * @param link
      * @param str
      * @return
      * @throws Exception 
      */

     public static String quote(java.sql.Connection link, String str)
           throws Exception
     {
         if (str == null) {
             return "NULL";
         }
         return "'"+mysql_real_escape_string(link,str)+"'";
     }

     /**
      * Escape identifier to protected against SQL Injection
      *
      * @param link
      * @param str
      * @return
      * @throws Exception 
      */

     public static String nameQuote(java.sql.Connection link, String str)
           throws Exception
     {
         if (str == null) {
             return "NULL";
         }
         return "`"+mysql_real_escape_string(link,str)+"`";
     }

 }