Java-防止SQL注入的转义字符串

Java-防止SQL注入的转义字符串,java,sql,regex,escaping,sql-injection,Java,Sql,Regex,Escaping,Sql Injection,我试图在java中加入一些反sql注入,但发现使用“replaceAll”字符串函数非常困难。最终,我需要一个函数,它可以将任何现有的\转换为\,将任何“转换为\”,将任何'转换为\',并将任何\n转换为\\n,这样当字符串被MySQL SQL注入计算时,就会被阻止 我已经升级了一些我正在使用的代码,所有函数中的”都让我的眼睛发狂。如果有人碰巧有这样的例子,我将不胜感激。防止SQL注入的唯一方法是使用参数化SQL。构建一个比那些以黑客SQL为生的人更聪明的过滤器是不可能的 因此,对所有输入、更新

我试图在java中加入一些反sql注入,但发现使用“replaceAll”字符串函数非常困难。最终,我需要一个函数,它可以将任何现有的
\
转换为
\
,将任何
转换为
\”
,将任何
'
转换为
\'
,并将任何
\n
转换为
\\n
,这样当字符串被MySQL SQL注入计算时,就会被阻止


我已经升级了一些我正在使用的代码,所有函数中的
”都让我的眼睛发狂。如果有人碰巧有这样的例子,我将不胜感激。

防止SQL注入的唯一方法是使用参数化SQL。构建一个比那些以黑客SQL为生的人更聪明的过滤器是不可能的


因此,对所有输入、更新和where子句使用参数。动态SQL只是为黑客打开了一扇大门,其中包括存储过程中的动态SQL。参数化,参数化,参数化。

使用正则表达式删除可能导致SQL注入的文本听起来像是SQL语句通过而不是SQL语句发送到数据库

首先,防止SQL注入的最简单方法之一是使用
PreparedStatement
,它接受数据以使用占位符替换为SQL语句,而不依赖字符串连接来创建要发送到数据库的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语句

对于不同的数据类型有不同的set方法——使用哪种方法取决于数据库字段。例如,如果数据库中有一个整数列,则应使用
setInt
方法。列出可用于设置和获取数据的所有不同方法。

如果您确实无法使用,或者,如果您不构建自己的工具,请使用。从托管在Google上的代码:

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

有关详细信息,请参阅和


请特别注意介绍项目的地方。

如果您正在处理遗留系统,或者您有太多的地方需要在太短的时间内切换到
PreparedStatement
s,即,如果使用其他答案建议的最佳实践存在障碍,您可以尝试(这是对OP在原始问题下的评论的回答;我完全同意PreparedStatement是这项工作的工具,而不是正则表达式。)

当你说
\n
时,你是指序列
\
+
n
还是实际的换行字符?如果是
\
+
n
,任务就相当简单:

s = s.replaceAll("['\"\\\\]", "\\\\$0");
要匹配输入中的一个反斜杠,请将其中四个放在正则表达式字符串中。要在输出中放置一个反斜杠,请将其中四个放在替换字符串中。这是假设您正在以Java字符串文本的形式创建正则表达式和替换。如果您以任何其他方式创建它们(例如,从文件中读取它们),你不必做那些双重逃跑

如果输入中有换行符,并且希望用转义序列替换它,则可以使用以下命令对输入进行第二次传递:

s = s.replaceAll("\n", "\\\\n");
或者你想要两个反斜杠(我不太清楚):


PreparedStatements是大多数情况下的方法,但并非所有情况下都是如此。有时,您会发现自己的查询或其中的一部分必须构建并存储为字符串以供以后使用。请查看上的,以了解更多详细信息和不同编程语言的API。

您需要以下代码。乍一看,这可能是错误的看起来像我编的任何旧代码。但是,我做的是查看源代码。然后,我仔细查看了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();
    }
}
公共类SQLInjectionEscaper{
公共静态字符串转义字符串(字符串x、布尔转义双引号){
StringBuilder sBuilder=新的StringBuilder(x.length()*11/10);
int stringLength=x.length();
对于(int i=0;ipublic 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();
    }
}
<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;
}
private String scrub(
    String parameter,
    int    length
    )
{
    String parm = null;

    if ( parameter != null && parameter.length() > 0 && parameter.length() < length )
    {
        parm = parameter
            .replace( "\\", " " )
            .replace( "\"", " " )
            .replace( "\'", " " )
            .replace( "\t", " " )
            .replace( "\r", " " )
            .replace( "\n", " " )
            .trim();
    }

    return parm;
}