連結を使用して文字列を作成することができます。そうしても、セキュリティ警告は発生しません。また、多くの行に分割して記述したほうがよい長い 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 警告を無視する必要があります。
結論: この質問の一般的なケースに対する完全な解決策はありません。