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

Sco*_*ner 142 java regex sql sql-injection escaping

我试图在java中放入一些反sql注入,并且发现很难使用"replaceAll"字符串函数.最后,我需要将任何现有的转换功能\\\,任何"\",任何'\',任何\n\\n使得当字符串由MySQL的SQL注入评估将被阻止.

我已经搞砸了一些我正在使用的代码,所有\\\\\\\\\\\功能都让我的眼睛变得疯狂.如果有人碰巧有这样的例子我会非常感激.

Kal*_*see 243

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
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

无论名称和电子邮件中包含哪些字符,这些字符都将直接放在数据库中.它们不会以任何方式影响INSERT语句.

对于不同的数据类型有不同的set方法 - 您使用哪种方法取决于您的数据库字段.例如,如果数据库中有INTEGER列,则应使用setInt方法. PreparedStatement文档列出了可用于设置和获取数据的所有不同方法.

  • 如果您要插入INTEGER字段,则需要使用'setInt'.同样,其他数字数据库字段将使用其他setter.我发布了一个指向PreparedStatement文档的链接,列出了所有setter类型. (2认同)
  • 是Cylon,PreparedStatements生成参数化查询. (2认同)
  • @Kaleb Brasee,谢谢。很高兴知道。这些工具在每种环境中都不同,但是深入了解参数化查询是基本答案。 (2认同)

Cyl*_*Cat 45

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

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

  • 顺便说一句,我不知道为什么没有提到这一点,但使用PreparedStatements也更容易**更具可读性.仅这一点可能使它们成为每个了解它们的程序员的默认设置. (16认同)
  • 甚至参数化SQL也不是100%的保证.但这是一个非常好的开始. (11认同)
  • @Cylon Cat:当然,当一大块SQL(如@WhereClause或@tableName)作为参数传递时,连接到SQL中,并动态执行.当您让用户编写代码时,就会发生SQL注入.是否将代码作为参数捕获并不重要. (3认同)
  • 请注意,某些数据库的PreparedStatements创建起来非常昂贵,因此如果您需要执行大量数据库,请测量这两种类型. (3认同)
  • @duffymo,我同意没有什么是100%安全的.你有一个SQL注入的例子,甚至可以使用参数化的SQL吗? (2认同)

Pas*_*ent 35

如果您确实无法使用防御选项1:准备语句(参数化查询)防御选项2:存储过程,请不要构建自己的工具,请使用OWASP Enterprise Security API.来自Google Code上托管的OWASP ESAPI:

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

有关更多详细信息,请参阅在JavaSQL注入防护备忘单中防止SQL注入.

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

  • 到目前为止,ESAPI似乎已经不复存在了.在AWS上有WAF,它可以帮助防止SQL注入,XSS等在这一点还有其他选择吗? (3认同)

Ala*_*ore 19

(这是对原始问题下OP的评论的回答;我完全同意PreparedStatement是这项工作的工具,而不是正则表达式.)

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

s = s.replaceAll("['\"\\\\]", "\\\\$0");
Run Code Online (Sandbox Code Playgroud)

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

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

s = s.replaceAll("\n", "\\\\n");
Run Code Online (Sandbox Code Playgroud)

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

s = s.replaceAll("\n", "\\\\\\\\n");
Run Code Online (Sandbox Code Playgroud)


小智 12

PreparedStatements是进入大多数情况的方式,但不是所有情况.有时您会发现自己处于需要构建查询或其中一部分的情况并将其存储为字符串以供以后使用.查看OWASP网站上的SQL注入预防备忘单,了解更多详细信息和不同编程语言的API.

  • OWASP 备忘单已移至 GitHub。SQL 注入备忘单现在位于:https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.md (3认同)

coo*_*ird 9

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

一,以防止在首位的SQL注入的最简单的方法是使用PreparedStatement,它接受数据使用占位符,它不依赖于字符串连接创建一个SQL语句发送到数据库替换到SQL语句.

欲了解更多信息,使用预处理语句Java教程将是一个良好的开端.


小智 9

准备好的语句是最好的解决方案,但如果你真的需要手动完成,你也可以使用StringEscapeUtilsApache Commons-Lang库中的类.它有一个escapeSql(String)方法,你可以使用:

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

  • 这已从最新版本中删除,因为它只是转义单引号 (8认同)
  • 这个答案应该删除,因为它不会阻止sql注入。 (5认同)
  • 供参考:https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/StringEscapeUtils.html#escapeSql(java.lang.String) 反正这个方法只转义引号并且似乎不能防止 SQL 注入攻击。 (2认同)

Boz*_*zho 6

如果您正在处理遗留系统,或者您有太多地方PreparedStatement在很短的时间内切换到s - 即如果使用其他答案建议的最佳实践存在障碍,您可以尝试AntiSQLFilter


Ric*_*ard 6

您需要以下代码.乍一看,这可能看起来像我编写的任何旧代码.但是,我所做的是查看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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为这段代码是上述链接中反编译的源代码版本.现在在更新的`mysql-connector-java-xxx`中,`case'\ u00a5'`和`case'\ u20a9'`语句似乎已被删除 (2认同)