它可以使用串聯來創建字符串。這樣做不會導致安全警告。並且爲了清晰起見,當處理較長的SQL語句時更好,因爲這些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警告。
結論:這個問題的一般情況沒有完美的解決方案。
嗨user714965,感謝您的回覆。我有其他使用StringBuilder的警告,所以這是行不通的。註釋的作用(向其他讀者澄清其完全限定的名稱是@ edu.umd.cs.findbugs.annotations.SuppressWarnings)是很好的。現在我必須決定是否需要我的項目中的FindBugs庫。再次感謝。 – ederribeiro
@ederribeiro:從項目中對FindBugs的依賴可能不是你所說的最好的。在我最初的回答中,我錯過了一點,請看最後一段。 – Kai
這解決了這個問題,因爲OP確信他的代碼對SQL注入是安全的。但它是容易出錯的,因此是一般的不好的做法。用參數準備的語句是要走的路。不會投票否定,因爲它實際上解決了問題,但我建議不要這樣做。 –