简体   繁体   English

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

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

I am using jdbc PreparedStatement for data insertion.我正在使用 jdbc PreparedStatement 进行数据插入。

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

tablename and columnString are something that is dynamically generated. tablenamecolumnString是动态生成的。

I've tried to parameterise tablename and columnString but they will just resolve to something like 'tablename' which will violate the syntax.我试图参数化 tablename 和 columnString,但它们只会解析为诸如 'tablename' 之类的东西,这将违反语法。

I've found somewhere online that suggest me to lookup the database to check for valid tablename/columnString, and cache it somewhere(a Hashset perhaps) for another query, but I'm looking for better performance/ quick hack that will solve the issue, perhaps a string validator/ regex that will do the trick.我在网上找到了建议我查找数据库以检查有效的表名/列字符串,并将其缓存在某处(可能是哈希集)以用于另一个查询,但我正在寻找更好的性能/快速破解来解决问题,也许是一个可以解决问题的字符串验证器/正则表达式。

Have anyone came across this issue and how do you solve it?有没有人遇到过这个问题,你是如何解决的?

I am not a java-guy, so, only a theory.我不是一个 java 人,所以,只是一个理论。

You can either format dynamically added identifiers or white-list them.您可以格式化动态添加的标识符或它们列入白名单

Second option is way better.第二种选择要好得多。 Because因为

  • most developers aren't familiar enough with identifiers to format them correctly.大多数开发人员对标识符不够熟悉,无法正确格式化它们。 Say, to quote an identifier, which is offered in the first comment, won't make it protected at all.比如说,引用第一个评论中提供的标识符,根本不会使其受到保护。
  • there could be another attack vector, not entirely an injection, but similar: imagine there is a column in your table, an ordinary user isn't allowed to - say, called "admin".可能还有另一个攻击向量,不完全是注入,但类似:假设您的表中有一个列,不允许普通用户 - 例如,称为“管理员”。 With dynamically built columnString using data coming from the client side, it's piece of cake to forge a privilege escalation.通过使用来自客户端的数据动态构建columnString ,打造特权升级是小菜一碟。

Thus, to list all the possible (and allowed) variants in your code beforehand, and then to verify entered value against it, would be the best.因此,事先在您的代码中列出所有可能(和允许)的变体,然后根据它验证输入的值,将是最好的。

As of columnString - is consists of separate column names.columnString - 由单独的列名组成。 Thus, to protect it, one have to verify each separate column name against a white list, and then assemble a final columnString from them.因此,为了保护它,必须对照白名单验证每个单独的列名称,然后从它们组装一个最终的columnString

Create a method that generates the sql string for you:创建一个为您生成 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());
}

Then use it...然后用它...

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

As an elaboration on @Anders' answer, don't use the input parameter as the name directly, but keep a properties file (or database table) that maps a set of allowed inputs to actual table names.作为对@Anders 回答的详细说明,不要直接使用输入参数作为名称,而是保留一个将一组允许的输入映射到实际表名的属性文件(或数据库表)。

That way any invalid name will not lead to valid SQL (and can be caught before any SQL is generated) AND the actual names are never known outside the application, thus making it far harder to guess what would be valid SQL statements.这样,任何无效的名称都不会导致有效的 SQL(并且可以在生成任何 SQL 之前被捕获)并且实际名称在应用程序之外永远不会知道,因此更难猜测什么是有效的 SQL 语句。

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

There are multiple solutions we can apply.我们可以应用多种解决方案。

1) White List Input Validation 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");
  • By doing this input validation on tableName, will allows only specified tables in the query, so it will prevents sql injection attack.通过对 tableName 执行此输入验证,将只允许查询中的指定表,因此它将防止 sql 注入攻击。

2) Bind your dynamic columnName(s) or tableName(s) with special characters as shown below 2) 将您的动态 columnName(s) 或 tableName(s) 与特殊字符绑定,如下所示

eg:例如:

  • For Mysql : use back codes (`)对于 Mysql:使用后退代码 (`)

    Select `columnName ` from `tableName `;从`tableName`中选择`columnName`;

  • For MSSQL : Use double codes(" or [ ] )对于 MSSQL:使用双代码(" 或 [ ] )

    select "columnName" from "tableName";从“tableName”中选择“columnName”;
    or或者
    select [columnName] from [tableName];从 [tableName] 中选择 [columnName];

Note: Before doing this you should sanitize your data with this special characters ( `, " , [ , ] )注意:在执行此操作之前,您应该使用此特殊字符(`、"、[、])清理您的数据

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

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