Java 等效于 PHP 的 mysql_real_escape_string()
Posted
技术标签:
【中文标题】Java 等效于 PHP 的 mysql_real_escape_string()【英文标题】:Java equivalent for PHP's mysql_real_escape_string() 【发布时间】:2010-11-04 08:01:37 【问题描述】:是否有与 php 的 mysql_real_escape_string() 等效的 Java?
这是为了在将 SQL 注入尝试传递给 Statement.execute() 之前对其进行转义。
我知道我可以使用 PreparedStatement 代替,但我们假设这些是一次性语句,因此准备它们将导致 lower performance。我已经将代码更改为使用 PreparedStatement,但鉴于现有代码的结构方式,escape() 函数将使代码更改更易于查看和维护;我更喜欢易于维护的代码,除非有令人信服的理由导致额外的复杂性。此外,数据库对 PreparedStatements 的处理方式也有所不同,因此这可能会使我们暴露于数据库中以前从未遇到过的错误,需要在发布到生产环境之前进行更多测试。
Apache StringEscapeUtils escapeSQL() 只转义单引号。
后记: 我继承的环境中有很多微妙之处是我在我的问题中故意避免的。
需要考虑的两点:
1) 准备好的语句不是万能的,不能提供 100% 的 SQL 注入保护。一些数据库驱动程序使用不安全的字符串连接来实例化参数化查询,而不是将查询预编译为二进制形式。此外,如果您的 SQL 依赖于存储过程,则需要确保存储过程本身不会以不安全的方式构建查询。
2) 大多数准备好的语句实现将语句绑定到实例化该语句的数据库连接。如果你使用数据库连接池,你需要小心 仅将准备好的语句引用与准备它的连接一起使用。一些池化机制确实透明地实现了这一点。否则,您也可以合并准备好的语句,或者(最简单但开销更大)为每个查询创建一个新的准备好的语句。
【问题讨论】:
您更喜欢易于维护的代码,但您更愿意使用手动字符串转义而不是 PrearedStatement? 鉴于现有代码(我没有编写)的方式,是的,它会更容易维护。 较低的性能可能比安全风险更有吸引力。安全惩罚可能太高了。在应用程序初始化代码中准备好你的语句,惩罚可能不明显(当然取决于应用程序)。 【参考方案1】:除了 PreparedStatement 之外,我不会相信任何其他东西来确保安全。但是,如果您在构建查询时需要类似的工作流程,您可以使用下面的代码。它在下面使用 PreparedStatement,像 StringBuilder 一样工作,添加转义函数并为您跟踪参数索引。可以这样使用:
SQLBuilder sqlBuilder = new SQLBuilder("update ").append(dbName).append(".COFFEES ");
sqlBuilder.append("set SALES = ").escapeString(sales);
sqlBuilder.append(", TOTAL = ").escapeInt(total);
sqlBuilder.append("where COF_NAME = ").escapeString(coffeeName);
sqlBuilder.prepareStatement(connection).executeUpdate();
代码如下:
class SQLBuilder implements Appendable
private StringBuilder sqlBuilder;
private List<Object> values = new ArrayList<>();
public SQLBuilder()
sqlBuilder = new StringBuilder();
public SQLBuilder(String str)
sqlBuilder = new StringBuilder(str);
@Override
public SQLBuilder append(CharSequence csq)
sqlBuilder.append(csq);
return this;
@Override
public SQLBuilder append(CharSequence csq, int start, int end)
sqlBuilder.append(csq, start, end);
return this;
@Override
public SQLBuilder append(char c)
sqlBuilder.append(c);
return this;
// you can add other supported parameter types here...
public SQLBuilder escapeString(String x)
protect(x);
return this;
public SQLBuilder escapeInt(int x)
protect(x);
return this;
private void escape(Object o)
sqlBuilder.append('?');
values.add(o);
public PreparedStatement prepareStatement(Connection connection)
throws SQLException
PreparedStatement preparedStatement =
connection.prepareStatement(sqlBuilder.toString());
for (int i = 0; i < values.size(); i++)
Object value = values.get(i);
// you can add other supported parameter types here...
if (value instanceof String)
preparedStatement.setString(i + 1, (String) value);
else if (value instanceof Integer)
preparedStatement.setInt(i + 1, (Integer) value);
return preparedStatement;
@Override
public String toString()
return "SQLBuilder: " + sqlBuilder.toString();
【讨论】:
【参考方案2】:根据 Daniel Schneller 的说法,在 Java 中没有标准的方法来处理 PHP 的 mysql_real_escape_string() 我所做的是链接 replaceAll 方法来处理避免任何异常所需的每个方面。这是我的示例代码:
public void saveExtractedText(String group,String content)
try
content = content.replaceAll("\\", "\\\\")
.replaceAll("\n","\\n")
.replaceAll("\r", "\\r")
.replaceAll("\t", "\\t")
.replaceAll("\00", "\\0")
.replaceAll("'", "\\'")
.replaceAll("\\"", "\\\"");
state.execute("insert into extractiontext(extractedtext,extractedgroup) values('"+content+"','"+group+"')");
catch (Exception e)
e.printStackTrace();
【讨论】:
【参考方案3】:这里有一些代码可以实现您正在寻找的东西。最初在 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)+"`";
【讨论】:
@droope 代码检查字符串中是否有任何危险字符。如果不是,则返回字符串而不进行处理。然后代码检查转义字符串是否通常会删除任何可能危险的字符。如果是这样,则以转义形式返回字符串。最后,转义的字符串被发送到 MySQL,由专门用于转义字符串的 QUOTE 函数进行转义。 @Ken V.H. 抱歉,该网站已经关闭了一段时间,所以我发布了原始代码。【参考方案4】:commons-lang.jar 中的 org.apache.commons.lang.StringEscapeUtils.class 可以解决你的问题!
【讨论】:
【参考方案5】:据我所知,没有“标准”的方法。
尽管您目前存在顾虑,但我强烈建议您使用准备好的语句。对性能的影响可以忽略不计 - 我们也有类似的情况,每秒有数千条语句 - 其中大多数也是一次性的。
您获得的安全性应该比您尚未见过的性能问题更重要。在我看来,这是“不要过早优化”的明显情况。
无论如何,如果您稍后真的发现遇到性能问题,请通过仔细分析确保准备好的语句确实是原因,然后寻找替代方案。在此之前,您应该省去尝试正确逃脱的麻烦。
这更重要,因为我推断您正在开发某种面向公众的网站 - 内部应用程序很少获得足够的流量来关注性能。
【讨论】:
【参考方案6】:避免 SQL 注入的唯一明智方法是使用预准备/参数化语句。
例如,您出于某种原因试图避免使用 PreparedStatement。如果你做一次性声明,准备它们的时间应该可以忽略不计(“一次性”和“性能关键”是矛盾的,恕我直言)。如果您在循环中执行操作,准备好的语句甚至会导致性能提高。
【讨论】:
【参考方案7】:不要假设 PreparedStatements 较慢。尝试,测量,然后判断。
PreparedStatements 应该总是优先于 Statement 使用,几乎无一例外,尤其是当您试图避免 SQL 注入攻击时。
【讨论】:
以上是关于Java 等效于 PHP 的 mysql_real_escape_string()的主要内容,如果未能解决你的问题,请参考以下文章
PHP等效于JavaScript getTime() [重复]