我的工作有一块类似下面的代码的项目:
String sql = "SELECT MAX(" + columnName + ") FROM " + tableName;
PreparedStatement ps = connection.prepareStatement(sql);
有什么办法,我可以改变这个代码,以便FindBugs的不停的给我一个 - 警示“安全已准备语句是从非恒定的字符串生成的”?
此外,假设这个代码是安全的有关SQL INJECTION,因为我可以控制代码别处“表名”和“列名”的可能值(它们不直接从用户输入来的)。
不要串联的sql
的字符串+
。 您可以使用
String sql = String.format("SELECT MAX(%s) FROM %s ", columnName, tableName);
这比连接字符串,所以你应该初始化这个较慢的static
那么这是没有问题的。
我认为使用StringBuilder
也将修复该警告。
你能避免此警告的另一种方法是添加@SuppressWarnings("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING")
该字符串(或方法/或类)以上。
你也可以使用一个过滤器文件来定义哪些应该被排除规则。
private static final String SQL = "SELECT MAX(?) FROM ?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.preparedStatement.setInt(1,columnName);
ps.preparedStatement.setString(2,tableName);
如果你正在使用事先准备好的声明,然后在参数应该是一个最终的字符串和参数应在以后使用SETINT,方法的SetString加入。
这将解决findbug警告。
尝试使用以下...
private static final String SQL = "SELECT MAX(%s) FROM %s";
然后使用的String.format()调用,当你使用它...
PreparedStatement ps = connection.prepareStatement(String.format(sql,columnName,tableName));
如果不解决这个问题,你总是可以忽略检查; 在FindBugs的配置将其关闭。
如果不工作(或者是不是一种选择),有些IDE(如的IntelliJ)也将让你suprress警告与任何特殊格式的注释或注解。
无论的String.format也不是的StringBuilder(或StringBuffer的)帮助我。
解决方案是“prepareStatement”隔离:
private PreparedStatement prepareStatement(Connection conn, String sql) throws SQLException {
return conn.prepareStatement(sql);
}
它可以使用串联来创建字符串。 这样做不会引起安全警告。 而随着这将更好的写法在许多线路拆分的长SQL语句打交道时是preferrable为清晰起见
使用变量构建字符串是什么原因造成了安全警告。
这将导致警告:
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的警告与任何其他的答案提出的方法。
结论 :对于这个问题,一般情况下,没有完美的解决方案。
如果您确保没有SQL注入的可能性,使用SuppressFBWarnings注释的方法:
@edu.umd.cs.findbugs.annotations.SuppressFBWarnings("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING")
文章来源: How to avoid “Security - A prepared statement is generated from a nonconstant String” FindBugs Warning