代码之家  ›  专栏  ›  技术社区  ›  Scott Bonner

防止SQL注入的Java字符串

  •  135
  • Scott Bonner  · 技术社区  · 14 年前

    我试图在爪哇中放置一些反SQL注入,并且发现很难使用“RealPoST”字符串函数。最终我需要一个函数来转换现有的 \ \\ 任何 " \" 任何 ' \' 和任何 \n \\n 这样当字符串被mysql计算时,SQL注入将被阻塞。

    我已经积累了一些我正在使用的代码 \\\\\\\\\\\ 在功能上让我的眼睛发疯。如果有人恰好有这样一个例子,我会非常感激。

    11 回复  |  直到 7 年前
        1
  •  234
  •   Kaleb Brasee    13 年前

    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语句。

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

        2
  •  45
  •   Cylon Cat    14 年前

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

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

        3
  •  33
  •   Pascal Thivent    14 年前

    如果你真的不能使用 Defense Option 1: Prepared Statements (Parameterized Queries) Defense Option 2: Stored Procedures ,不要构建自己的工具,使用 OWASP Enterprise Security API . 从 OWASP ESAPI 托管于谷歌代码:

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

    有关详细信息,请参阅 Preventing SQL Injection in Java SQL Injection Prevention Cheat Sheet .

    特别注意 Defense Option 3: Escaping All User Supplied Input 介绍了 OWASP ESAPI 项目)。

        4
  •  18
  •   Alan Moore Chris Ballance    11 年前

    (这是对OP在原始问题下的评论的回应;我完全同意PreparedStatement是此工作的工具,而不是Regex。)

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

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

    要匹配输入中的一个反斜杠,可以在regex字符串中放置四个反斜杠。要在输出中放一个反斜杠,可以在替换字符串中放四个。假设您正在以Java字符串文字的形式创建正则表达式和替换项。如果您以任何其他方式创建它们(例如,通过从文件中读取它们),则不必执行所有的双重转义。

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

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

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

    s = s.replaceAll("\n", "\\\\\\\\n");
    
        5
  •  13
  •   user566057    13 年前

    准备声明在大多数情况下都是可行的,但不是所有情况。有时,您会发现自己处于这样一种情况:查询或其一部分必须作为字符串进行构建和存储,以供以后使用。退房 SQL Injection Prevention Cheat Sheet OWASP Site 有关不同编程语言的更多详细信息和API。

        6
  •  9
  •   coobird    14 年前

    使用正则表达式删除可能导致SQL注入的文本,听起来就像是通过 Statement 而不是 PreparedStatement .

    首先,防止SQL注入的最简单方法之一是使用 准备声明 ,它接受使用占位符将数据替换为SQL语句,而不依赖字符串连接来创建要发送到数据库的SQL语句。

    更多信息, Using Prepared Statements The Java Tutorials 会是一个很好的开始的地方。

        7
  •  7
  •   ktbiz    7 年前

    准备好的语句是最好的解决方案,但是如果确实需要手动执行,也可以使用 StringEscapeUtils 从Apache Commons Lang库中获取类。它有一个 escapeSql(String) 方法,您可以使用:

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

        8
  •  6
  •   Bozho Michał Mech    14 年前

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

        9
  •  6
  •   Richard    8 年前

    您需要下面的代码。乍一看,这可能看起来像我编的任何旧代码。但是,我所做的是查看 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();
        }
    }
    
        10
  •  0
  •   shareef Brian Agnew    8 年前

    在搜索了大量的解决方案以防止SQL注入后,如果遗留系统不能在每一个地方应用准备好的语句。

    java-security-cross-site-scripting-xss-and-sql-injection topic 是解决办法吗

    我尝试了@Richard的解决方案,但在我的案例中没有起作用。 我用过滤嘴

    此筛选器的目标是将请求包装为自己的代码 包装myhttpRequestWrapper,它转换:

    将具有特殊字符(<、>、_、_)的HTTP参数转换为HTML 代码通过org.springframework.web.util.htmlutils.htmlescape(_) 方法。注意:在ApacheCommons中有类似的E类: 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;
        }
    }
    
        11
  •  0
  •   Billcountry    7 年前

    来源:[来源]

    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;
    

    }