处理动态表/列名时如何防止SQL注入?

时间:2015-10-01 08:39:00

标签: java sql jdbc sql-injection

我正在使用jdbc PreparedStatement进行数据插入。

Statement stmt = conn.prepareStatement(
"INESRT INTO" + tablename+ "("+columnString+") VALUES (?,?,?)");

tablename columnString 是动态生成的内容。

我已尝试参数化tablename和columnString,但它们只会解析为类似'tablename'的内容,这会违反语法。

我在网上发现某个地方建议我查找数据库以检查有效的tablename / columnString,并将其缓存到某个地方(也许是一个Hashset)用于另一个查询,但我正在寻找更好的性能/快速入侵解决问题,也许是一个字符串验证器/正则表达式,可以解决问题。

有没有人遇到过这个问题,你是如何解决的?

5 个答案:

答案 0 :(得分:1)

我不是一个java人,所以,只是一个理论。

您可以格式化动态添加的标识符或 white-list

第二种选择更好。因为

  • 大多数开发人员对标识符不够熟悉,无法正确格式化。比如,引用第一条评论中提供的标识符,根本不会让它受到保护。
  • 可能有另一个攻击向量,不完全是注入,但类似:假设你的表中有一列,普通用户不允许 - 比方说,叫做#34; admin"。使用来自客户端的数据动态构建columnString,可以打造特权升级。

因此,要预先在代码中列出所有可能的(和允许的)变体,然后根据它验证输入的值,这将是最好的。

columnString开始 - 由单独的列名组成。因此,为了保护它,必须针对白名单验证每个单独的列名称,然后从它们汇编最终columnString

答案 1 :(得分:0)

创建一个为您生成sql字符串的方法:

private static final String template = "insert into %s (%s) values (%s)";

private String buildStmt(String tblName, String ... colNames) {
    StringJoiner colNamesJoiner = new StringJoiner(",");
    StringJoiner paramsJoiner = new StringJoiner(",");
    Arrays.stream(colNames).forEach(colName -> {
        colNamesJoiner.add(colName);
        paramsJoiner.add("?");
    });
    return String.format(template, tblName, colNamesJoiner.toString(), paramsJoiner.toString());
}

然后使用它......

Statement stmt = conn.prepareStatement(buildStmt(tablename, [your column names]));

答案 2 :(得分:0)

作为@Anders答案的详细说明,不要直接使用输入参数作为名称,而是保留一个属性文件(或数据库表),将一组允许的输入映射到实际的表名。

这样,任何无效的名称都不会导致有效的SQL(并且可以在生成任何SQL之前捕获)并且实际名称在应用程序之外永远不会被知道,因此很难猜出什么是有效的SQL语句。

答案 3 :(得分:0)

我认为,最好的方法是从数据库或其他非用户输入中获取表名和列名,并在准备好的语句中使用其余参数。

答案 4 :(得分:0)

我们可以应用多种解决方案。

1)白名单输入验证

String tableName;
switch(PARAM):
   case "Value1": tableName = "fooTable";
                  break;
   case "Value2": tableName = "barTable";
              break;
   ...

   default      : throw new InputValidationException("unexpected value provided for table name");
  • 通过对tableName执行此输入验证,将仅允许查询中指定的表,因此它将阻止sql注入攻击。

2)使用特殊字符绑定动态columnName(s)或tableName(s),如下所示

   例如:

  • 对于Mysql:使用后面的代码(`)
      

    从`tableName`中选择`columnName`;

  • 对于MSSQL:使用双重代码(“或[])
      

    从“tableName”中选择“columnName”;

      从[tableName]中选择[columnName];

注意:在执行此操作之前,您应使用此特殊字符(`,“,[,])

清理数据