繁体   English   中英

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

[英]How to prevent SQL injection when dealing with dynamic table/column names?

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

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

tablenamecolumnString是动态生成的。

我试图参数化 tablename 和 columnString,但它们只会解析为诸如 'tablename' 之类的东西,这将违反语法。

我在网上找到了建议我查找数据库以检查有效的表名/列字符串,并将其缓存在某处(可能是哈希集)以用于另一个查询,但我正在寻找更好的性能/快速破解来解决问题,也许是一个可以解决问题的字符串验证器/正则表达式。

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

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

您可以格式化动态添加的标识符或它们列入白名单

第二种选择要好得多。 因为

  • 大多数开发人员对标识符不够熟悉,无法正确格式化它们。 比如说,引用第一个评论中提供的标识符,根本不会使其受到保护。
  • 可能还有另一个攻击向量,不完全是注入,但类似:假设您的表中有一个列,不允许普通用户 - 例如,称为“管理员”。 通过使用来自客户端的数据动态构建columnString ,打造特权升级是小菜一碟。

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

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

创建一个为您生成 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]));

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

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

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

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

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];

注意:在执行此操作之前,您应该使用此特殊字符(`、"、[、])清理您的数据

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM