# HG changeset patch # User František Kučera # Date 1328703323 -3600 # Node ID 3b4abb1ec5a3360017e4bc79c8f6f9e9efc8b2fd # Parent fdeb54809e238b7da2bfeeb299efe5b8d46f9658 Limit počtu řádků (10000) a doby provádění SQL dotazu (3 vteřiny) v pískovišti, nedokonalá ochrana proti DoS útoku (kartézský součin, náročný dotaz). diff -r fdeb54809e23 -r 3b4abb1ec5a3 java/sql-vyuka/src/java/cz/frantovo/sql/vyuka/dao/PiskovisteDAO.java --- a/java/sql-vyuka/src/java/cz/frantovo/sql/vyuka/dao/PiskovisteDAO.java Wed Feb 08 12:44:51 2012 +0100 +++ b/java/sql-vyuka/src/java/cz/frantovo/sql/vyuka/dao/PiskovisteDAO.java Wed Feb 08 13:15:23 2012 +0100 @@ -14,118 +14,149 @@ /** * Pro spouštění uživatelových příkazů. + * * @author fiki */ public class PiskovisteDAO extends VyukaSuperDAO { - private enum VLASTNOSTI { + /** maximální doba trvání SQL dotazu – vteřiny */ + private static final int LIMIT_ČASU = 3; + /** maximální počet řádků */ + private static final int LIMIT_POČTU = 10000; - VYCHOZI_CESTA - } - TipyDAO tipy = new TipyDAO(); - HistorieDAO historie = new HistorieDAO(); + private enum VLASTNOSTI { - public VysledekSQL vykonejSQL(String sql, Uzivatel uzivatel) { - VysledekSQL v = new VysledekSQL(); - if (historie.ulozPrikaz(sql, uzivatel)) { + VYCHOZI_CESTA, + LIMIT_ČASU + } + TipyDAO tipy = new TipyDAO(); + HistorieDAO historie = new HistorieDAO(); - Connection db = getSpojeni(DATABAZE.PISKOVISTE); - if (db == null) { - v.getHlasky().add(new Hlaska("Došlo k chybě spojení.", Typ.Chyba)); - } else { - PreparedStatement ps = null; - ResultSet rs = null; - try { - /** - * Uživatelskému SQL příkazu předřadíme výchozí cestu (search_path). - * Protože uživatelé si ji mohou měnit a kvůli recyklaci databázových zdrojů - * by jeden uživatel mohl ovlivnit jiného. - */ - if (getVlastnost(VLASTNOSTI.VYCHOZI_CESTA) != null) { - sql = orizni(getVlastnost(VLASTNOSTI.VYCHOZI_CESTA)) + sql; - } + public VysledekSQL vykonejSQL(String sql, Uzivatel uzivatel) { + VysledekSQL v = new VysledekSQL(); + if (historie.ulozPrikaz(sql, uzivatel)) { - long casPred = System.currentTimeMillis(); - ps = db.prepareStatement(sql); - boolean isRS = ps.execute(); + Connection db = getSpojeni(DATABAZE.PISKOVISTE); + if (db == null) { + v.getHlasky().add(new Hlaska("Došlo k chybě spojení.", Typ.Chyba)); + } else { + PreparedStatement ps = null; + ResultSet rs = null; + try { + /** + * Uživatelskému SQL příkazu předřadíme výchozí cestu (search_path). + * Protože uživatelé si ji mohou měnit a kvůli recyklaci databázových zdrojů + * by jeden uživatel mohl ovlivnit jiného. + */ + if (getVlastnost(VLASTNOSTI.VYCHOZI_CESTA) != null) { + sql = orizni(getVlastnost(VLASTNOSTI.VYCHOZI_CESTA)) + sql; + } + + /** + * TODO: + * použít ps.setQueryTimeout(LIMIT_ČASU); + * až ho bude podporovat JDBC ovladač, + * viz níže. + * Uživatel ale stejně může zadat: + * SET statement_timeout 0; + * do svého SQL dotazu. + */ + if (getVlastnost(VLASTNOSTI.LIMIT_ČASU) != null) { + sql = orizni(getVlastnost(VLASTNOSTI.LIMIT_ČASU)) + sql; + } - if (isRS) { - rs = ps.getResultSet(); - v.getTabulky().add(zpracujVysledek(rs)); - } + long casPred = System.currentTimeMillis(); + ps = db.prepareStatement(sql); + /** + * Limit času bohužel není podporován JDBC ovladačem. + * Alespoň ne v postgresql-9.1-901.jdbc4.jar + * http://jdbc.postgresql.org/todo.html + * + * TODO: + * ps.setQueryTimeout(LIMIT_ČASU); + */ + ps.setMaxRows(LIMIT_POČTU); + boolean isRS = ps.execute(); - /** - * Ošetříme případ, kdy uživatel zadá SQL příkaz, který nevrací výsledkovou sadu. - * Typicky nastavení výchozího schématu: SET search_path = '…'; - * Poznámka: jeden „SET search_path TO "…"“ se obvykle předřazuje uživatelskému SQL (viz PiskovisteDAO.xml). - */ - while (ps.getMoreResults() || ps.getUpdateCount() > -1) { - rs = ps.getResultSet(); - if (rs == null) { - /** Jedná se o „update count“. */ - } else { - v.getTabulky().add(zpracujVysledek(rs)); - } - } - long dobaProvadeni = System.currentTimeMillis() - casPred; + if (isRS) { + rs = ps.getResultSet(); + v.getTabulky().add(zpracujVysledek(rs)); + } - /** Varování */ - if (v.getHlasky().size() < 1 && v.getTabulky().size() < 1) { - v.getHlasky().add(new Hlaska("SQL příkaz proběhl, ale nevrátil žádná data.", Typ.Varovani)); - } + /** + * Ošetříme případ, kdy uživatel zadá SQL příkaz, který nevrací výsledkovou + * sadu. + * Typicky nastavení výchozího schématu: SET search_path = '…'; + * Poznámka: jeden „SET search_path TO "…"“ se obvykle předřazuje uživatelskému + * SQL (viz PiskovisteDAO.xml). + */ + while (ps.getMoreResults() || ps.getUpdateCount() > -1) { + rs = ps.getResultSet(); + if (rs == null) { + /** Jedná se o „update count“. */ + } else { + v.getTabulky().add(zpracujVysledek(rs)); + } + } + long dobaProvadeni = System.currentTimeMillis() - casPred; - /** Varování */ - int pocitadloTabulek = 1; - for (Tabulka t : v.getTabulky()) { - if (t.getHodnoty().size() < 1) { - v.getHlasky().add(new Hlaska("Tabulka " + pocitadloTabulek + " je prázdná.", Typ.Varovani)); - } - pocitadloTabulek++; - } + /** Varování */ + if (v.getHlasky().size() < 1 && v.getTabulky().size() < 1) { + v.getHlasky().add(new Hlaska("SQL příkaz proběhl, ale nevrátil žádná data.", Typ.Varovani)); + } - v.getHlasky().add(new Hlaska("SQL příkaz byl proveden úspěšně, během " + dobaProvadeni + " ms.", Typ.OK)); + /** Varování */ + int pocitadloTabulek = 1; + for (Tabulka t : v.getTabulky()) { + if (t.getHodnoty().size() < 1) { + v.getHlasky().add(new Hlaska("Tabulka " + pocitadloTabulek + " je prázdná.", Typ.Varovani)); + } + pocitadloTabulek++; + } - } catch (SQLException e) { - log.log(Level.SEVERE, "SQL chyba při vykonávání uživatelského dotazu.", e); - v.getHlasky().add(new Hlaska("Chybné SQL: " + e.getMessage(), Typ.Chyba)); - } catch (Exception e) { - log.log(Level.SEVERE, "Chyba při vykonávání uživatelského dotazu.", e); - v.getHlasky().add(new Hlaska("Došlo k chybě dotazu.", Typ.Chyba)); - } finally { - zavri(db, ps, rs); - } - } + v.getHlasky().add(new Hlaska("SQL příkaz byl proveden úspěšně, během " + dobaProvadeni + " ms.", Typ.OK)); - /** Tip pro uživatele */ - String tip = tipy.getTip(); - if (tip != null) { - v.getHlasky().add(new Hlaska(tip, Typ.Tip, false)); - } + } catch (SQLException e) { + log.log(Level.SEVERE, "SQL chyba při vykonávání uživatelského dotazu.", e); + v.getHlasky().add(new Hlaska("Chybné SQL: " + e.getMessage(), Typ.Chyba)); + } catch (Exception e) { + log.log(Level.SEVERE, "Chyba při vykonávání uživatelského dotazu.", e); + v.getHlasky().add(new Hlaska("Došlo k chybě dotazu.", Typ.Chyba)); + } finally { + zavri(db, ps, rs); + } + } - } else { - v.getHlasky().add(new Hlaska("Došlo k chybě historie.", Typ.Chyba)); - } - return v; - } + /** Tip pro uživatele */ + String tip = tipy.getTip(); + if (tip != null) { + v.getHlasky().add(new Hlaska(tip, Typ.Tip, false)); + } - private Tabulka zpracujVysledek(ResultSet rs) throws SQLException { - Tabulka t = new Tabulka(); + } else { + v.getHlasky().add(new Hlaska("Došlo k chybě historie.", Typ.Chyba)); + } + return v; + } - int pocetSloupecku = rs.getMetaData().getColumnCount(); - String[] zahlavi = new String[pocetSloupecku]; - t.setZahlavi(zahlavi); - for (int i = 0; i < pocetSloupecku; i++) { - zahlavi[i] = rs.getMetaData().getColumnName(i + 1); - } + private Tabulka zpracujVysledek(ResultSet rs) throws SQLException { + Tabulka t = new Tabulka(); - while (rs.next()) { - Object[] hodnoty = new Object[pocetSloupecku]; - for (int i = 0; i < pocetSloupecku; i++) { - hodnoty[i] = rs.getObject(i + 1); - } - t.getHodnoty().add(hodnoty); - } + int pocetSloupecku = rs.getMetaData().getColumnCount(); + String[] zahlavi = new String[pocetSloupecku]; + t.setZahlavi(zahlavi); + for (int i = 0; i < pocetSloupecku; i++) { + zahlavi[i] = rs.getMetaData().getColumnName(i + 1); + } - return t; - } + while (rs.next()) { + Object[] hodnoty = new Object[pocetSloupecku]; + for (int i = 0; i < pocetSloupecku; i++) { + hodnoty[i] = rs.getObject(i + 1); + } + t.getHodnoty().add(hodnoty); + } + + return t; + } } diff -r fdeb54809e23 -r 3b4abb1ec5a3 java/sql-vyuka/src/java/cz/frantovo/sql/vyuka/dao/PiskovisteDAO.xml --- a/java/sql-vyuka/src/java/cz/frantovo/sql/vyuka/dao/PiskovisteDAO.xml Wed Feb 08 12:44:51 2012 +0100 +++ b/java/sql-vyuka/src/java/cz/frantovo/sql/vyuka/dao/PiskovisteDAO.xml Wed Feb 08 13:15:23 2012 +0100 @@ -2,12 +2,23 @@ - + - + + + + + \ No newline at end of file