12

我正在开发一个具有如下代码的项目:

String sql = "SELECT MAX(" + columnName + ") FROM " + tableName;                
PreparedStatement ps = connection.prepareStatement(sql);

有什么方法可以更改此代码,以便 FindBugs 停止给我“安全性 - 从非常量字符串生成准备好的语句”警告?

请假设这段代码对于 SQL INJECTION 是安全的,因为我可以在代码的其他地方控制“tableName”和“columnName”的可能值(它们不直接来自用户输入)。

4

7 回答 7

7

不要sql通过 连接字符串+。您可以使用

String sql = String.format("SELECT MAX(%s) FROM %s ", columnName, tableName);

这比连接字符串要慢,所以你应该初始化它static然后这不是问题。

我认为使用 aStringBuilder也可以解决此警告。

避免此警告的另一种方法是在@SuppressWarnings("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING")该字符串(或方法/或类)之上添加。

您还可以使用过滤器文件来定义应排除的规则。

于 2012-05-08T14:13:58.090 回答
7
private static final String SQL = "SELECT MAX(?) FROM ?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.preparedStatement.setInt(1,columnName);
ps.preparedStatement.setString(2,tableName);

如果您使用准备好的语句,那么 in 参数应该是最终字符串,并且稍后应该使用 setInt、setString 方法添加参数。

这将解决 findbug 警告。

于 2013-10-08T11:23:00.647 回答
1

尝试使用以下...

private static final String SQL = "SELECT MAX(%s) FROM %s";

然后在使用时使用 String.format() 调用...

PreparedStatement ps = connection.prepareStatement(String.format(sql,columnName,tableName));

如果那不能解决问题,您可以随时忽略该检查;在您的 FindBugs 配置中将其关闭。

如果这不起作用(或不是一个选项),某些 IDE(如 IntelliJ)也会让您使用特殊格式的注释或注释来抑制警告。

于 2012-05-08T14:16:19.317 回答
1

String.format 和 StringBuilder(或 StringBuffer)都没有帮助我。

解决方案是“prepareStatement”隔离:

private PreparedStatement prepareStatement(Connection conn, String sql) throws SQLException {
    return conn.prepareStatement(sql);
}
于 2013-03-29T07:31:14.670 回答
1

可以使用连接来创建您的字符串。这样做不会导致安全警告。在处理较长的 SQL 语句时,为了清楚起见,最好将其拆分为多行

使用变量构造字符串是导致安全警告的原因。

这将导致警告:

String columnName = getName();
String tableName  = getTableName();
final String sql = "SELECT MAX(" + columnName + ") FROM " + tableName;
PreparedStatement ps = connection.prepareStatement(sql);

这不起作用:

String columnName = getName();
String tableName  = getTableName();
final String sql = "SELECT MAX(" + "?" + ")" +
                   "FROM " + "?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, columnName);
ps.setString(2, tableName);

它不起作用,因为准备好的语句只允许将参数绑定到 SQL 语句的“值”位。

这是有效的解决方案:

private static final boolean USE_TEST_TABLE = true;
private static final boolean USE_RESTRICTED_COL = true;
private static final String TEST_TABLE = "CLIENT_TEST";
private static final String PROD_TABLE = "CLIENT";
private static final String RESTRICTED_COL ="AGE_COLLATED";
private static final String UNRESTRICTED_COL ="AGE";

....................

final String sql = "SELECT MAX(" +
        ( USE_RESTRICTED_COL ? RESTRICTED_COL : UNRESTRICTED_COL ) +  ")" +
        "FROM " +
        ( USE_TEST_TABLE ? TEST_TABLE : PROD_TABLE );
PreparedStatement ps = connectComun.prepareStatement(sql);

但它仅在您必须在编译时已知名称的两个表之间进行选择时才有效。您可以在超过 2 种情况下使用复合三元运算符,但随后它变得不可读。

如果 getName() 或 getTableName() 从不受信任的来源获取名称,则第一种情况可能是安全问题。

如果这些变量之前已经过验证,则很有可能使用变量构建安全的 SQL 语句。这是您的情况,但 FindBugs 无法弄清楚。Findbugs 无法知道哪些来源可信或不可信。

但是,如果您必须使用来自用户或不受信任的输入的列名或表名,那么就没有办法了。您必须使用其他答案中提出的任何方法来验证自己的此类字符串并忽略 Findbugs 警告。

结论:对于这个问题的一般情况,没有完美的解决方案。

于 2015-07-09T09:49:24.997 回答
0

如果您确定没有 SQL 注入的可能性,请在方法上使用SuppressFBWarnings注释:

@edu.umd.cs.findbugs.annotations.SuppressFBWarnings("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING")
于 2015-02-27T11:44:25.660 回答
0
StringBuilder sql = new StringBuilder();
sql.append("SELECT MAX(")
   .append(columnName)
   .append(") FROM ")
   .append(tableName);

PreparedStatement ps = connection.prepareStatement(sql);
ps.execute();
于 2020-01-17T19:48:54.693 回答