2012-05-08 11 views
6

sto lavorando su un progetto che ha un pezzo di codice come quella qui sotto:Come evitare "Sicurezza - Una dichiarazione preparata viene generato da una stringa non costante" FindBugs Attenzione

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

C'è un modo che Posso cambiare questo codice in modo che FindBugs smetta di darmi un avviso "Sicurezza - Un'istruzione preparata viene generata da una stringa non costante"?

Si presuma che questo codice sia sicuro per quanto riguarda SQL INJECTION poiché posso controllare altrove nel codice i possibili valori per "tableName" e "columnName" (non vengono provengono direttamente dall'input dell'utente).

risposta

3

Non concatenare la stringa sql per +. È possibile utilizzare

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

Questo è più lento di concatenare una stringa così si dovrebbe inizializzare questo static allora questo non è un problema.

Penso che l'utilizzo di un StringBuilder correggerà anche questo avviso.

Un altro modo per evitare questo avviso è aggiungere @SuppressWarnings("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING") sopra quella stringa (o il metodo/o la classe).

È inoltre possibile utilizzare un Filter File per definire regole da escludere.

+0

Ciao utente714965, grazie per la risposta. Ho altre occorrenze di questo avviso usando StringBuilder, in modo che non funzioni. L'annotazione funziona (è bene chiarire ad altri lettori che il suo nome completo è @ edu.umd.cs.findbugs.annotations.SuppressWarnings). Ora devo decidere se voglio la lib di FindBug nel mio progetto o no. Grazie ancora. – ederribeiro

+0

@ederribeiro: una dipendenza da FindBugs dal tuo progetto potrebbe non essere la migliore che hai ragione. Nella mia risposta originale ho perso un punto, per favore vedi l'ultimo paragrafo. – Kai

+2

Questo risolve questo particolare problema perché OP è sicuro che il suo codice è sicuro da SQL INJECTION. Ma è soggetto a errori e quindi una cattiva pratica in generale. Le dichiarazioni preparate con parametri sono la strada da percorrere. Non voterò negativo dato che in realtà risolve la domanda ma consiglio di non farlo in quel modo. –

1

Provare a utilizzare il seguente ...

private static final String SQL = "SELECT MAX(%s) FROM %s"; 

E quindi utilizzando uno String.Format() chiami quando lo si utilizza ...

PreparedStatement ps = connection.prepareStatement(String.format(sql,columnName,tableName)); 

Se questo non risolve il problema , puoi sempre ignorare quel controllo; spegnilo nella tua configurazione di FindBugs.

Se ciò non funziona (o non è un'opzione), alcuni IDE (come IntelliJ) consentono anche di sovrascrivere gli avvisi con commenti o annotazioni appositamente formattati.

+0

Ciao Jesse, grazie per la risposta. Ho provato a fare come hai detto ma sfortunatamente non ha funzionato. Il tuo ultimo suggerimento, lo stesso che @ user714965 ha menzionato, fa il trucco. Ora devo decidere se voglio pagare il prezzo di avere la lib di FindBug nel mio progetto solo per sbarazzarmi di avvertimenti. – ederribeiro

1

Né String.format né StringBuilder (o StringBuffer) mi hanno aiutato.

soluzione era "prepareStatement" isolamento:

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

Anche questo non mi ha aiutato. – 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); 

se si utilizza istruzione preparata, quindi nel parametro dovrebbe essere una stringa finale e parametri devono essere aggiunti in seguito utilizzando setInt, setString metodi.

risolverà l'avviso findbug.

+1

hai esecrato la tua affermazione? Il nome della tabella vincolante si ottiene 'ORA-00903: nome tabella non valido '. Il nome della colonna di passaggio funziona, ma si ottiene di nuovo il nome della colonna (non il valore della colonna). Questo è NOGO! –

0

Se è assicurarsi che c'è possibilità di SQL injection, utilizzare il SuppressFBWarnings annotazione sul metodo:

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

E 'possibile utilizzare la concatenazione per creare la vostra stringa. Ciò non causa l'avviso di sicurezza.Ed è preferibile per chiarezza quando si tratta di istruzioni SQL lunghe che potrebbero essere meglio suddivise su più righe

L'utilizzo delle variabili per costruire la stringa è ciò che causa l'avviso di sicurezza.

Ciò causerà l'avvertimento:

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

Non non funziona:

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

Non funziona perché le dichiarazioni preparate consentono solo parametri da rilegare per "valori" bit del Istruzione SQL.

Questa è la soluzione che funziona:

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

ma funziona solo se si dispone di scegliere tra due tabelle i cui nomi sono noti al momento della compilazione. È possibile utilizzare operatori ternari composti per più di 2 casi, ma diventa illeggibile.

Il primo caso potrebbe essere un problema di sicurezza se getName() o getTableName() ottiene il nome da fonti non attendibili.

È abbastanza possibile creare un'istruzione SQL sicura utilizzando le variabili se tali variabili sono state precedentemente verificate. Questo è il tuo caso, ma FindBugs non riesce a capirlo. Findbugs non è in grado di sapere quali fonti sono attendibili o meno.

Ma se è necessario utilizzare un nome di colonna o tabella dall'utente o un input non affidabile, non c'è modo di aggirarlo. Devi verificare te stesso tale stringa e ignorare l'avviso di Findbugs con uno dei metodi proposti in altre risposte.

Conclusione: Non esiste una soluzione perfetta per il caso generale di questa domanda.

+0

Ciao @ jose-antonio-dura-olmos grazie per il tuo suggerimento ma, ci hai provato? Non sembra possibile impostare "metadati" (come nomi di tabelle o nomi di colonne) usando istruzioni preparate. Ho provato con mysql e h2 senza successo. C'è anche un utente in questo thread che ha provato con Oracle e ha anche fallito. – ederribeiro

+0

Ho un codice di produzione che cambia i nomi delle tabelle in questo modo. Non ho controllato questo particolare codice, ma lo farò. –

+0

Questo è ciò che accade per parlare di memoria. Avevo bisogno di scegliere tra due tavoli, uno per la produzione e uno per il test. Ma non ha funzionato in questo modo poiché i nomi di tabelle e colonne non possono essere variabili; la frase precompilata deve conoscere i nomi di tabella e colonna utilizzati per verificare la sintassi corretta. Quindi non possono essere variabili. Sono arrivato a una soluzione che mi permetteva comunque di utilizzare la concatenazione senza ricevere l'avviso. Ho aggiornato la mia risposta con quella soluzione. –

Problemi correlati