Java - 转义字符串以防止SQL注入
-
06-07-2019 - |
题
我正在尝试在java中使用一些反sql注入,并且发现很难使用<!>“replaceAll <!>”;字符串函数。最终我需要一个函数将任何现有的\
转换为\\
,任何"
转换为\"
,任何'
转换为\'
,以及任何\n
转换为\\n
,以便在字符串时由MySQL SQL评估将被阻止。
我已经搞砸了我正在使用的一些代码,而且函数中的所有\\\\\\\\\\\
都让我的眼睛变得疯狂。如果有人碰巧有这样的例子我会非常感激。
解决方案
PreparedStatements是可行的方法,因为它们使SQL注入变得不可能。这是一个将用户输入作为参数的简单示例:
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
}
}
}
无论姓名和电子邮件中包含哪些字符,这些字符都将直接放在数据库中。它们不会以任何方式影响INSERT语句。
对于不同的数据类型有不同的设置方法 - 您使用哪种方法取决于您的数据库字段。例如,如果数据库中有INTEGER列,则应使用setInt
方法。 PreparedStatement文档列出了所有可用的不同方法用于设置和获取数据。
其他提示
防止SQL注入的唯一方法是使用参数化SQL。只是不可能构建一个比以黑客为生的人更聪明的过滤器。
因此,请对所有输入,更新和where子句使用参数。动态SQL只是黑客的一扇门,它包括存储过程中的动态SQL。参数化,参数化,参数化。
如果确实你不能使用防御选项1:准备好的语句(参数化查询)或防御选项2:存储过程,不要构建自己的工具,使用 OWASP Enterprise Security API 。来自Google代码托管的 OWASP ESAPI :
Don <!>#8217;编写自己的安全控件!在为每个Web应用程序或Web服务开发安全控件时重新发明轮子会导致浪费时间和大量安全漏洞。 OWASP企业安全API(ESAPI)工具包可帮助软件开发人员防范安全<!>#8208;相关的设计和实现缺陷。
有关详细信息,请参阅防止Java中的SQL注入和 SQL注入预防备忘单。
特别注意防御选项3:转义所有用户提供的输入介绍 OWASP ESAPI 项目。)
(这是对原始问题下OP的评论的回答;我完全同意PreparedStatement是这项工作的工具,而非正则表达式。)
当你说\n
时,你的意思是序列\
+ n
还是实际的换行符?如果它是<=> + <=>,则任务非常简单:
s = s.replaceAll("['\"\\\\]", "\\\\$0");
要匹配输入中的一个反斜杠,请将其中的四个放入正则表达式字符串中。要在输出中放入一个反斜杠,可以将其中的四个放入替换字符串中。这假设您正在以Java String文字的形式创建正则表达式和替换。如果您以任何其他方式创建它们(例如,通过从文件中读取它们),则不必执行所有双重转义。
如果您在输入中有换行符并且想要用转义序列替换它,则可以使用以下命令对输入进行第二次传递:
s = s.replaceAll("\n", "\\\\n");
或许你想要两个反斜杠(我不太清楚):
s = s.replaceAll("\n", "\\\\\\\\n");
PreparedStatements是进入大多数情况的方式,但不是所有情况。有时您会发现自己处于需要构建查询或其中一部分的情况并将其存储为字符串以供以后使用。查看 SQL注入预防备忘单 www.owasp.org/“rel =”noreferrer“> OWASP网站了解更多详细信息和不同编程语言的API。
使用正则表达式删除可能导致SQL注入的文本听起来像SQL语句通过 Statement
而不是 PreparedStatement
。
首先防止SQL注入的最简单方法之一是使用<=>,它接受使用占位符替换为SQL语句的数据,这不依赖于字符串连接来创建SQL语句发送到数据库。
准备好的声明是最好的解决方案,但如果你真的需要手动完成,你也可以使用 StringEscapeUtils
类。它有一个 escapeSql(String)
方法,您可以使用:
import org.apache.commons.lang.StringEscapeUtils;
…
String escapedSQL = StringEscapeUtils.escapeSql(unescapedSQL);
如果您正在处理遗留系统,或者您有太多地方在很短的时间内切换到PreparedStatement
s - 即如果使用其他答案建议的最佳做法存在障碍,您可以尝试 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逃脱的字符列表,那么知道这一点在安全方面确实令人感到安慰。也许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();
}
}
在搜索大量的解决方案以防止sqlmap从sql注入之后,如果遗留系统无法在每个地方应用准备好的语句。
java-security-cross- site-scripting-xss-and-sql-injection主题 是解决方案
我试过@Richard的解决方案,但在我的情况下不起作用。 我用过滤器
此过滤器的目标是将请求包装为自己编码的 包装器MyHttpRequestWrapper转换:
带有特殊字符的HTTP参数(<!> lt;,<!> gt;,<!>#8216;,<!>#8230;)到HTML 代码通过org.springframework.web.util.HtmlUtils.htmlEscape(<!>#8230;) 方法。注意:Apache Commons中有类似的classe: org.apache.commons.lang.StringEscapeUtils.escapeHtml(<!>#8230;)SQL 注入字符(<!>#8216;,<!>#8220;,<!>#8230;)通过Apache Commons classe org.apache.commons.lang.StringEscapeUtils.escapeSql(<!> 8230#)
<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;
}