12

以下のようなコードを含むプロジェクトに取り組んでいます。

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

このコードを変更して、FindBugs が「セキュリティ - 準備されたステートメントが非定数文字列から生成されました」という警告を出さないようにする方法はありますか?

"tableName" と "columnName" の可能な値をコードの他の場所で制御できるため、このコードは SQL インジェクションに関して安全であると想定してください (それらはユーザー入力から直接取得されるわけではありません)。

4

7 に答える 7

7

sql文字列を。で連結しないでください+。使用できます

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

これは文字列を連結するよりも遅いので、これを初期化する必要があります。そうすればstatic、これは問題になりません。

StringBuilderを使用すると、この警告も修正されると思います。

この警告を回避する別の方法は、@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

連結を使用して文字列を作成することができます。そうしても、セキュリティ警告は発生しません。また、多くの行に分割して記述したほうがよい長い 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 つのテーブルから選択する必要がある場合にのみ機能します。複合三項演算子を 2 つ以上のケースで使用できますが、その後は判読できなくなります。

getName() または getTableName() が信頼できないソースから名前を取得する場合、最初のケースはセキュリティ上の問題である可能性があります。

変数が事前に検証されている場合、変数を使用して安全な SQL ステートメントを作成することは十分に可能です。これはあなたの場合ですが、FindBugs はそれを理解できません。Findbugs は、どのソースが信頼できるかどうかを知ることができません。

ただし、ユーザーまたは信頼できない入力からの列またはテーブル名を使用する必要がある場合は、それを回避する方法はありません。そのような文字列を自分で確認し、他の回答で提案されている方法のいずれかで Findbugs 警告を無視する必要があります。

結論: この質問の一般的なケースに対する完全な解決策はありません。

于 2015-07-09T09:49:24.997 に答える
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 に答える
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 に答える
0

SQL インジェクションの可能性がないことを確認する場合は、メソッドでSuppressFBWarningsアノテーションを使用します。

@edu.umd.cs.findbugs.annotations.SuppressFBWarnings("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING")
于 2015-02-27T11:44:25.660 に答える