2009-11-04 17 views
15

Dla niektórych SQL nie można używać przygotowaną statment, na przykład:Jak to zrobić bez użycia zdezynfekować SQL przygotowanych sprawozdań

SELECT MAX(AGE) FROM ? 

Na przykład, kiedy chcę się zmieniać tabelę. Czy istnieje narzędzie, które oczyszcza sql w Javie? Jest jeden w rubinie.

+6

Jeśli nazwa tabeli pochodzi bezpośrednio ** ** od danych wprowadzonych przez użytkownika, masz dużo większe problemy niż martwić się o swoją odkażania SQL (a jeśli nie ma nic do dezynfekcji) – ChssPly76

Odpowiedz

2

Nie możliwe. Najlepsze co możesz zrobić, to użyć String#format().

String sql = "SELECT MAX(AGE) FROM %s"; 
sql = String.format(sql, tablename); 

Należy pamiętać, że nie powoduje to ryzyka wstrzyknięcia SQL. Jeśli wartość tablename jest wartością kontrolowaną przez użytkownika/klienta, należy ją odseparować za pomocą String#replaceAll().

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

Mam nadzieję, że to pomoże.

[Edytuj] Powinienem dodać: NIE używaj tego dla wartości kolumn, gdzie możesz użyć PreparedStatement dla. Po prostu kontynuuj używanie go w zwykły sposób dla dowolnych wartości kolumn.

[Edycja 2] Najlepiej byłoby nie pozwolić użytkownikowi/klientowi na wprowadzenie nazwy tabeli w żądany sposób, ale lepiej przedstawić menu rozwijane zawierające wszystkie prawidłowe nazwy klatek (które można uzyskać przez DatabaseMetaData#getCatalogs()) w interfejsie użytkownika, aby użytkownik/klient może go wybrać. Nie zapomnij sprawdzić po stronie serwera, jeśli wybór jest ważny, ponieważ można sfałszować parametry żądania.

+0

@BalusC - +1 dla odniesienia SQL Injection. –

0

W tym przypadku można sprawdzić nazwę tabeli przed listą dostępnych tabel, pobierając listing tabeli z DatabaseMetaData. W rzeczywistości prawdopodobnie łatwiej byłoby użyć wyrażenia regularnego do stripowania spacji, być może także niektórych słów zastrzeżonych sql, ";" itp. Z ciągu znaków przed użyciem czegoś w stylu String.format do zbudowania pełnej instrukcji sql.

Powodem, dla którego nie można użyć parametru prepareStatement, jest prawdopodobnie umieszczenie nazwy tabeli w '' s i uniknięcie jej w postaci ciągu.

17

Prawidłowe, przygotowane parametry zapytania do wyciągu mogą być używane tylko wtedy, gdy użyjemy pojedynczej literalnej wartości o wartości. Nie można użyć parametru dla nazwy tabeli, nazwy kolumny, listy wartości ani żadnej innej składni SQL.

Musisz więc interpolować zmienną aplikacji do łańcucha SQL i odpowiednio zacytować ciąg. Używaj cytując do wytyczają swój identyfikator nazwa tabeli i uciec ciąg cytat podwojenie go:

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

Na przykład, jeśli nazwa tabeli jest dosłownie table"name, a RDBMS identyfikator cytat postać jest ", następnie sql powinien zawierać ciąg jak:

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

zgadzam się również z użytkownika @ ChssPly76 komentarza - najlepiej, jeśli dane wejściowe użytkownika nie jest właściwie dosłowne nazwa tabeli, ale znaczącym, że kod mapy do nazwy tabeli, który cię następnie inter polate do zapytania SQL. Zapewnia to większą pewność, że nie wystąpi iniekcja SQL.

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 za komentarz na temat map kodu. To zdecydowanie jest droga. Wszystkie nazwy tablenames można uzyskać przez DatabaseMetaData # getCatalogs() i być reprezentowane jako rozwijane w interfejsie użytkownika. – BalusC

+0

Ale jeśli podajesz nazwy prawdziwych tabel w rozwijanym menu, nadal powinieneś używać mapy do konwersji danych wejściowych użytkownika na nazwę tabeli, ponieważ dane wejściowe mogą być fałszywe. Na przykład. Mogę wpisać adres URL z dowolną potrzebą w parametrach żądania, niezależnie od tego, co pojawia się w menu rozwijanym. Używanie mapy ma na celu filtrowanie danych wejściowych po otrzymaniu żądania, a nie przed wysłaniem formularza interfejsu użytkownika. –