Java – 转义string,以防止SQL注入

我试图把一些反SQL注入在Java中,我发现很难与“replaceAll”string函数工作。 最终,我需要一个函数,将任何现有的\ \\ ,任何" \"\"任何\'和任何\n\\n以便当string被MySQL评估时,SQL注入将被阻止。

我已经拿起了一些我正在使用的代码,所有的函数都让我的眼睛变得疯狂。 如果有人碰巧有这样的例子,我将不胜感激。

PreparedStatements是要走的路,因为它们使SQL注入变得不可能。 下面是一个以用户input为参数的简单示例:

 public insertUser(String name, String email) { Connection conn = null; PreparedStatement stmt = null; try { conn = setupTheDatabaseConnectionSomehow(); stmt = conn.prepareStatement("INSERT INTO person (name, email) values (?, ?)"); stmt.setString(1, name); stmt.setString(2, email); stmt.executeUpdate(); } finally { try { if (stmt != null) { stmt.close(); } } catch (Exception e) { // log this error } try { if (conn != null) { conn.close(); } } catch (Exception e) { // log this error } } } 

不pipe名字和电子邮件中的字符是什么,这些字符都会直接放在数据库中。 它们不会以任何方式影响INSERT语句。

对于不同的数据types有不同的设置方法 – 你使用哪一种取决于你的数据库字段。 例如,如果数据库中有INTEGER列,则应使用setInt方法。 PreparedStatement文档列出了可用于设置和获取数据的所有不同方法。

防止SQL注入的唯一方法是使用参数化的SQL。 简单地说,构build一个比以SQL为生的人更聪明的filter是不可能的。

所以使用所有input,更新和where子句的参数。 dynamicSQL只是黑客的入门,包括存储过程中的dynamicSQL。 参数化,参数化,参数化。

如果确实无法使用防御选项1:预备语句(参数化查询)或防御选项2:存储过程 ,则不要使用OWASP Enterprise Security API构build自己的工具。 来自Google Code上的OWASP ESAPI :

不要写自己的安全控制! 当涉及到为每个Web应用程序或Web服务开发安全控制时,重新发明轮子会导致浪费时间和大量安全漏洞。 OWASP企业安全API(ESAPI)工具包帮助软件开发人员防范安全相关的devise和实施缺陷。

有关更多详细信息,请参阅防止Java中的 SQL注入和SQL注入预防备忘单 。

请特别注意防御选项3:转义引入OWASP ESAPI项目的所有用户提供的input )。

(这是回答OP在原始问题下的评论;我完全同意PreparedStatement是这个工作的工具,而不是正则expression式。)

当你说\n ,你的意思是序列\ + n还是一个实际的换行字符? 如果它是\ + n ,这个任务是非常简单的:

 s = s.replaceAll("['\"\\\\]", "\\\\$0"); 

要匹配input中的一个反斜杠,可以将其中的四个放在正则expression式string中。 要把一个反斜杠放在输出中,你把它们放在replacestring中。 这是假设你正在创build正则expression式和Java String文字forms的replace。 如果以其他方式创build它们(例如通过从文件中读取它们),则不必执行所有的双重转义操作。

如果在input中有一个换行字符,并且想用换码序列replace它,则可以使用以下命令对input进行第二遍:

 s = s.replaceAll("\n", "\\\\n"); 

或者,也许你想要两个反斜杠(我不太清楚):

 s = s.replaceAll("\n", "\\\\\\\\n"); 

PreparedStatements是大多数情况下的方式,但不是所有的情况。 有时你会发现自己处于一个查询或其一部分必须被构build并存储为一个string供以后使用的情况。 查看OWASP站点上的SQL注入预防备忘单以获得更多的细节和不同编程语言的API。

使用正则expression式来删除可能会导致SQL注入的文本,听起来像SQL语句正在通过Statement而不是PreparedStatement发送到数据库。

防止SQL注入的最简单方法之一是使用PreparedStatement ,它接受数据以使用占位符代替SQL语句,而不依赖于string连接来创build要发送到数据库的SQL语句。

欲了解更多信息,从Java教程中 使用准备好的语句将是一个很好的开始。

如果你正在处理一个遗留系统,或者你有太多的地方在很短的时间内切换到PreparedStatement – 也就是说,如果使用其他答案build议的最佳实践存在障碍,你可以尝试使用AntiSQLFilter

您需要下面的代码。 乍一看,这可能看起来像我编写的任何旧代码。 但是,我所做的是查看http://grepcode.com/file/repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.31/com/mysql/jdbc/PreparedStatement的源代码。; java 。 之后,我仔细查看了setString(int parameterIndex,String x)的代码来查找它所转义的字符,并将其自定义到我自己的类中,以便它可以用于您需要的目的。 毕竟,如果这是Oracle逃脱的angular色列表,那么知道这确实是安全的。 也许Oracle需要微调来为下一个主要Java版本添加一个类似于这个的方法。

 public class SQLInjectionEscaper { public static String escapeString(String x, boolean escapeDoubleQuotes) { StringBuilder sBuilder = new StringBuilder(x.length() * 11/10); int stringLength = x.length(); for (int i = 0; i < stringLength; ++i) { char c = x.charAt(i); switch (c) { case 0: /* Must be escaped for 'mysql' */ sBuilder.append('\\'); sBuilder.append('0'); break; case '\n': /* Must be escaped for logs */ sBuilder.append('\\'); sBuilder.append('n'); break; case '\r': sBuilder.append('\\'); sBuilder.append('r'); break; case '\\': sBuilder.append('\\'); sBuilder.append('\\'); break; case '\'': sBuilder.append('\\'); sBuilder.append('\''); break; case '"': /* Better safe than sorry */ if (escapeDoubleQuotes) { sBuilder.append('\\'); } sBuilder.append('"'); break; case '\032': /* This gives problems on Win32 */ sBuilder.append('\\'); sBuilder.append('Z'); break; case '\u00a5': case '\u20a9': // escape characters interpreted as backslash by mysql // fall through default: sBuilder.append(c); } } return sBuilder.toString(); } } 

Prepared Statements是最好的解决scheme,但是如果你真的需要手动完成,你也可以使用Apache Commons-Lang库中的StringEscapeUtils类。 它有一个escapeSql(String)方法,你可以使用:

import org.apache.commons.lang.StringEscapeUtils; … String escapedSQL = StringEscapeUtils.escapeSql(unescapedSQL);

search了一个testing从SQL注入防止sqlmap的解决scheme,如果遗留系统不能在每个地方应用准备的陈述。

java-security-cross-site-scripting-xss-and-sql-injection主题是解决scheme

我试了@理查德的解决scheme,但没有在我的情况下工作。 我用filter

这个filter的目标是将请求封装到一个自己编码的包装器MyHttpRequestWrapper中,该包装器将转换:

通过org.springframework.web.util.HtmlUtils.htmlEscape(…)方法将包含特殊字符(<,>,',…)的HTTP参数转换为HTML代码。 注意:在Apache Commons中有类似的类:org.apache.commons.lang.StringEscapeUtils.escapeHtml(…)SQL注入字符(',“,…)通过Apache Commons类org.apache.commons.lang.StringEscapeUtils。 escapeSql(…)

 <filter> <filter-name>RequestWrappingFilter</filter-name> <filter-class>com.huo.filter.RequestWrappingFilter</filter-class> </filter> <filter-mapping> <filter-name>RequestWrappingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> package com.huo.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletReponse; import javax.servlet.http.HttpServletRequest; public class RequestWrappingFilter implements Filter{ public void doFilter(ServletRequest req, ServletReponse res, FilterChain chain) throws IOException, ServletException{ chain.doFilter(new MyHttpRequestWrapper(req), res); } public void init(FilterConfig config) throws ServletException{ } public void destroy() throws ServletException{ } } package com.huo.filter; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import org.apache.commons.lang.StringEscapeUtils; public class MyHttpRequestWrapper extends HttpServletRequestWrapper{ private Map<String, String[]> escapedParametersValuesMap = new HashMap<String, String[]>(); public MyHttpRequestWrapper(HttpServletRequest req){ super(req); } @Override public String getParameter(String name){ String[] escapedParameterValues = escapedParametersValuesMap.get(name); String escapedParameterValue = null; if(escapedParameterValues!=null){ escapedParameterValue = escapedParameterValues[0]; }else{ String parameterValue = super.getParameter(name); // HTML transformation characters escapedParameterValue = org.springframework.web.util.HtmlUtils.htmlEscape(parameterValue); // SQL injection characters escapedParameterValue = StringEscapeUtils.escapeSql(escapedParameterValue); escapedParametersValuesMap.put(name, new String[]{escapedParameterValue}); }//end-else return escapedParameterValue; } @Override public String[] getParameterValues(String name){ String[] escapedParameterValues = escapedParametersValuesMap.get(name); if(escapedParameterValues==null){ String[] parametersValues = super.getParameterValues(name); escapedParameterValue = new String[parametersValues.length]; // for(int i=0; i<parametersValues.length; i++){ String parameterValue = parametersValues[i]; String escapedParameterValue = parameterValue; // HTML transformation characters escapedParameterValue = org.springframework.web.util.HtmlUtils.htmlEscape(parameterValue); // SQL injection characters escapedParameterValue = StringEscapeUtils.escapeSql(escapedParameterValue); escapedParameterValues[i] = escapedParameterValue; }//end-for escapedParametersValuesMap.put(name, escapedParameterValues); }//end-else return escapedParameterValues; } } 

自:[来源]

 public String MysqlRealScapeString(String str){ String data = null; if (str != null && str.length() > 0) { str = str.replace("\\", "\\\\"); str = str.replace("'", "\\'"); str = str.replace("\0", "\\0"); str = str.replace("\n", "\\n"); str = str.replace("\r", "\\r"); str = str.replace("\"", "\\\""); str = str.replace("\\x1a", "\\Z"); data = str; } return data; 

}