How to avoid “Security - A prepared statement is generated from a nonconstant String” FindBugs Warning

前端 未结 7 2810
灰色年华
灰色年华 2021-02-19 21:39

I am working on a project that has a piece of code like the one below:

String sql = \"SELECT MAX(\" + columnName + \") FROM \" + tableName;                
Prepa         


        
7条回答
  •  無奈伤痛
    2021-02-19 22:08

    It is possible to use concatenation to create your String. Doing so does not cause the Security warning. And is preferrable for clarity's sake when dealing with long SQL statements which would be better written split across many lines

    Using variables to construct the String is what causes the security warning.

    This will cause the warning :

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

    This will not not work :

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

    It does not work because prepared statements only allow parameters to be bound for "values" bits of the SQL statement.

    This is the solution that works :

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

    But it only works if you have to chose between two tables whose names are known at compile time. You could use compounded ternary operators for more than 2 cases but then it becomes unreadable.

    The 1st case might be a security issue if getName() or getTableName() gets the name from untrusted sources.

    It is quite possible to construct a secure SQL statement using variables if those variables have been previously verified. This is your case, but FindBugs can't figure it out. Findbugs is not able to know what sources are trusted or not.

    But if you must use a column or table name from user or untrusted input then there is no way around it. You have to verify yourself such string and ignore the Findbugs warning with any of the methods proposed in other answers.

    Conclusion : There is no perfect solution for the general case of this question.

提交回复
热议问题