2009-11-04 14 views
15

Per alcune istruzioni SQL non è possibile usare uno statment preparato, per esempio:Come faccio a disinfettare SQL senza utilizzare le istruzioni preparate

SELECT MAX(AGE) FROM ? 

Per esempio quando voglio variare il tavolo. Esiste un'utilità che disinfetta sql in Java? Ce n'è uno in rubino.

+6

Se il nome della tabella viene ** ** direttamente dall'input dell'utente, hai problemi molto più grandi di cui preoccuparsi che sanificazione SQL (e se non c'è niente da disinfettare) – ChssPly76

risposta

2

Non possibile. La cosa migliore che puoi fare è usare String#format().

Si noti che ciò non evita i rischi di iniezione SQL. Se tablename è un valore controllato dall'utente/client, è necessario disinfettarlo utilizzando String#replaceAll().

tablename = tablename.replaceAll("[^\\w]", ""); 

Spero che questo aiuti.

[Modifica] Devo aggiungere: NON usare questo per valori di colonna dove è possibile utilizzare PreparedStatement per. Continua semplicemente ad usarlo nel modo consueto per qualsiasi valore di colonna.

[Edit2] migliore sarebbe quella di non lasciare che l'utente/cliente sia in grado di entrare nel nometabella il modo in cui vogliono, ma meglio presente un menu a discesa contenente tutti tablenames validi (che si può ottenere con DatabaseMetaData#getCatalogs()) nell'interfaccia utente in modo che l'utente/cliente può selezionarlo. Non dimenticare di controllare sul lato server se la selezione è valida perché uno potrebbe falsificare i parametri della richiesta.

+0

@BalusC - +1 per il riferimento SQL Injection. –

0

In questo caso è possibile convalidare il nome della tabella rispetto all'elenco delle tabelle disponibili, ottenendo l'elenco delle tabelle da DatabaseMetaData. In realtà sarebbe probabilmente più facile usare una regex per spogliare gli spazi, magari anche alcune parole riservate sql, ";", ecc dalla stringa prima di usare qualcosa liek String.format per costruire la tua istruzione sql completa.

Il motivo per cui non è possibile utilizzare preparedStatement è perché probabilmente racchiude il nome della tabella in "s" e lo sfugge come una stringa.

17

I parametri di query dell'istruzione a destra e preparati possono essere utilizzati solo dove si utilizza un valore letterale singolo. Non è possibile utilizzare un parametro per un nome di tabella, un nome di colonna, un elenco di valori o qualsiasi altra sintassi SQL.

Quindi è necessario interpolare la variabile dell'applicazione nella stringa SQL e citare la stringa in modo appropriato. Non utilizzare citando per delimitare l'identificatore nome della tabella, e la fuga la stringa di citazione raddoppiando esso:

java.sql.DatabaseMetaData md = conn.getMetaData(); 
String q = md.getIdentifierQuoteString(); 
String sql = "SELECT MAX(AGE) FROM %s%s%s"; 
sql = String.format(sql, q, tablename.replaceAll(q, q+q), q); 

Ad esempio, se il nome della tabella è letteralmente table"name, e il tuo personaggio identificatore citazione RDBMS è ", allora sql dovrebbe contenere una stringa come:

SELECT MAX(AGE) FROM "table""name" 

sono anche d'accordo con @ di ChssPly76 commento - è meglio se l'input utente non è in realtà il nome della tabella letterale, ma un significante che il codice associa in un nome di tabella, che si allora inter polare nella query SQL. Questo ti dà una maggiore sicurezza che nessuna iniezione SQL può verificarsi.

HashMap h = new HashMap<String,String>(); 
/* user-friendly table name maps to actual, ugly table name */ 
h.put("accounts", "tbl_accounts123"); 

userTablename = ... /* user input */ 
if (h.containsKey(userTablename)) { 
    tablename = h.get(userTablename); 
} else { 
    throw ... /* Exception that user input is invalid */ 
} 
String sql = "SELECT MAX(AGE) FROM %s"; 
/* we know the table names are safe because we wrote them */ 
sql = String.format(sql, tablename); 
+0

+1 per il tuo commento sulle mappe del codice. Questa è sicuramente la strada da percorrere. Tutti i nomi di tabelle possono essere ottenuti da DatabaseMetaData # getCatalogs() ed essere rappresentati come un dropdown nell'interfaccia utente. – BalusC

+0

Ma se assegni i nomi delle tabelle reali in un menu a discesa, dovresti comunque utilizzare una mappa per convertire l'input dell'utente in un nome di tabella, perché l'input può essere falsificato. Per esempio. Posso digitare un URL con qualsiasi cosa desideri nei parametri della richiesta, indipendentemente da ciò che appare nel menu a discesa. L'uso di una mappa ha lo scopo di filtrare l'input dopo aver ricevuto la richiesta, non prima che emetta il modulo dell'interfaccia utente. –

Problemi correlati