2012-05-08 48 views
6

我在具有類似下面的一段代碼一個項目:如何避免「安全 - 已準備語句是從非恆定的字符串生成的」 FindBugs的警告

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

有沒有什麼辦法,我可以更改此代碼,以便FindBugs停止給我一個 「安全 - 準備好的語句是從非常量字符串生成的」警告?

請假定這段代碼對於SQL INJECTION是安全的,因爲我可以在代碼的其他地方控制「tableName」和「columnName」的可能 值(它們不會直接來自用戶輸入)。

回答

3

請勿連接sql字符串+。您可以使用

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

這比連接字符串,所以你應該初始化這個static那麼這是沒有問題的慢。

我認爲使用StringBuilder也會解決這個警告。

另一種避免此警告的方法是在字符串(或方法/或類)上添加@SuppressWarnings("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING")

您也可以使用Filter File來定義應排除的規則。

+0

嗨user714965,感謝您的回覆。我有其他使用StringBuilder的警告,所以這是行不通的。註釋的作用(向其他讀者澄清其完全限定的名稱是@ edu.umd.cs.findbugs.annotations.SuppressWarnings)是很好的。現在我必須決定是否需要我的項目中的FindBugs庫。再次感謝。 – ederribeiro

+0

@ederribeiro:從項目中對FindBugs的依賴可能不是你所說的最好的。在我最初的回答中,我錯過了一點,請看最後一段。 – Kai

+2

這解決了這個問題,因爲OP確信他的代碼對SQL注入是安全的。但它是容易出錯的,因此是一般的不好的做法。用參數準備的語句是要走的路。不會投票否定,因爲它實際上解決了問題,但我建議不要這樣做。 –

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)也可以讓您使用特殊格式的註釋或註釋來顯示警告。

+0

嗨,傑西,謝謝你的回覆。我試圖按照你的說法去做,但不幸的是它沒有奏效。你的最後一個建議,就像@ user714965提到的那樣,就是這樣做的。現在我必須決定是否要在我的項目中支付FindBugs lib的價格,以便擺脫警告。 – ederribeiro

1

既沒有String.format也沒有StringBuilder(或StringBuffer)幫助我。

溶液「prepareStatement」隔離:

private PreparedStatement prepareStatement(Connection conn, String sql) throws SQLException { 
    return conn.prepareStatement(sql); 
} 
+0

這也沒有幫助我。 – Lenymm

4
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警告。

+1

你是否在執行你的陳述?綁定表名稱會得到'ORA-00903:無效的表名'。傳遞列名稱的作品,但你得到列名稱(不是列值)。這是NOGO! –

0

如果您確保沒有SQL注入的可能性,使用SuppressFBWarnings標註的方法:

@edu.umd.cs.findbugs.annotations.SuppressFBWarnings("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING") 
1

它可以使用串聯來創建字符串。這樣做不會導致安全警告。並且爲了清晰起見,當處理較長的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警告。

結論:這個問題的一般情況沒有完美的解決方案。

+0

你好@ jose-antonio-dura-olmos謝謝你的建議,但是,你試過了嗎?似乎沒有可能使用準備好的語句來設置「元數據」(例如表名或列名)。我試過用mysql和h2沒有成功。此線程中還有一位用戶嘗試使用Oracle,但也失敗了。 – ederribeiro

+0

我有這樣改變表名的生產代碼。我沒有檢查這個特定的代碼,但我會這樣做。 –

+0

這是發生內存不足的原因。我需要在兩個表格中進行選擇,一個用於生產,另一個用於測試。但是它不能以這種方式工作,因爲表名和列名不可變;句子預編譯需要知道用於驗證正確sintax的表和列名。因此它們不能變化。我得到了一個解決方案,它仍然允許我使用連接而不會收到警告。我已經用該解決方案更新了我的答案。 –

相關問題