Changeset 78:3b4abb1ec5a3

Show
Ignore:
Timestamp:
02/08/12 13:15:23 (8 years ago)
Author:
František Kučera <franta-hg@…>
Branch:
default
Message:

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

Location:
java/sql-vyuka/src/java/cz/frantovo/sql/vyuka/dao
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • java/sql-vyuka/src/java/cz/frantovo/sql/vyuka/dao/PiskovisteDAO.java

    r53 r78  
    1515/** 
    1616 * Pro spouštění uživatelových příkazů. 
     17 * 
    1718 * @author fiki 
    1819 */ 
    1920public class PiskovisteDAO extends VyukaSuperDAO { 
    2021 
    21     private enum VLASTNOSTI { 
     22        /** maximální doba trvání SQL dotazu – vteřiny */ 
     23        private static final int LIMIT_ČASU = 3; 
     24        /** maximální počet řádků */ 
     25        private static final int LIMIT_POČTU = 10000; 
    2226 
    23         VYCHOZI_CESTA 
    24     } 
    25     TipyDAO tipy = new TipyDAO(); 
    26     HistorieDAO historie = new HistorieDAO(); 
     27        private enum VLASTNOSTI { 
    2728 
    28     public VysledekSQL vykonejSQL(String sql, Uzivatel uzivatel) { 
    29         VysledekSQL v = new VysledekSQL(); 
    30         if (historie.ulozPrikaz(sql, uzivatel)) { 
     29                VYCHOZI_CESTA, 
     30                LIMIT_ČASU 
     31        } 
     32        TipyDAO tipy = new TipyDAO(); 
     33        HistorieDAO historie = new HistorieDAO(); 
    3134 
    32             Connection db = getSpojeni(DATABAZE.PISKOVISTE); 
    33             if (db == null) { 
    34                 v.getHlasky().add(new Hlaska("Došlo k chybě spojení.", Typ.Chyba)); 
    35             } else { 
    36                 PreparedStatement ps = null; 
    37                 ResultSet rs = null; 
    38                 try { 
    39                     /** 
    40                      * Uživatelskému SQL příkazu předřadíme výchozí cestu (search_path). 
    41                      * Protože uživatelé si ji mohou měnit a kvůli recyklaci databázových zdrojů 
    42                      * by jeden uživatel mohl ovlivnit jiného. 
    43                      */ 
    44                     if (getVlastnost(VLASTNOSTI.VYCHOZI_CESTA) != null) { 
    45                         sql = orizni(getVlastnost(VLASTNOSTI.VYCHOZI_CESTA)) + sql; 
    46                     } 
     35        public VysledekSQL vykonejSQL(String sql, Uzivatel uzivatel) { 
     36                VysledekSQL v = new VysledekSQL(); 
     37                if (historie.ulozPrikaz(sql, uzivatel)) { 
    4738 
    48                     long casPred = System.currentTimeMillis(); 
    49                     ps = db.prepareStatement(sql); 
    50                     boolean isRS = ps.execute(); 
     39                        Connection db = getSpojeni(DATABAZE.PISKOVISTE); 
     40                        if (db == null) { 
     41                                v.getHlasky().add(new Hlaska("Došlo k chybě spojení.", Typ.Chyba)); 
     42                        } else { 
     43                                PreparedStatement ps = null; 
     44                                ResultSet rs = null; 
     45                                try { 
     46                                        /** 
     47                                         * Uživatelskému SQL příkazu předřadíme výchozí cestu (search_path). 
     48                                         * Protože uživatelé si ji mohou měnit a kvůli recyklaci databázových zdrojů 
     49                                         * by jeden uživatel mohl ovlivnit jiného. 
     50                                         */ 
     51                                        if (getVlastnost(VLASTNOSTI.VYCHOZI_CESTA) != null) { 
     52                                                sql = orizni(getVlastnost(VLASTNOSTI.VYCHOZI_CESTA)) + sql; 
     53                                        } 
     54                                         
     55                                        /** 
     56                                         * TODO: 
     57                                         * použít ps.setQueryTimeout(LIMIT_ČASU); 
     58                                         * až ho bude podporovat JDBC ovladač, 
     59                                         * viz níže. 
     60                                         * Uživatel ale stejně může zadat: 
     61                                         * SET statement_timeout 0; 
     62                                         * do svého SQL dotazu. 
     63                                         */ 
     64                                        if (getVlastnost(VLASTNOSTI.LIMIT_ČASU) != null) { 
     65                                                sql = orizni(getVlastnost(VLASTNOSTI.LIMIT_ČASU)) + sql; 
     66                                        } 
    5167 
    52                     if (isRS) { 
    53                         rs = ps.getResultSet(); 
    54                         v.getTabulky().add(zpracujVysledek(rs)); 
    55                     } 
     68                                        long casPred = System.currentTimeMillis(); 
     69                                        ps = db.prepareStatement(sql); 
     70                                        /** 
     71                                         * Limit času bohužel není podporován JDBC ovladačem. 
     72                                         * Alespoň ne v postgresql-9.1-901.jdbc4.jar 
     73                                         * http://jdbc.postgresql.org/todo.html 
     74                                         *  
     75                                         * TODO: 
     76                                         * ps.setQueryTimeout(LIMIT_ČASU); 
     77                                         */ 
     78                                        ps.setMaxRows(LIMIT_POČTU); 
     79                                        boolean isRS = ps.execute(); 
    5680 
    57                     /** 
    58                      * Ošetříme případ, kdy uživatel zadá SQL příkaz, který nevrací výsledkovou sadu. 
    59                      * Typicky nastavení výchozího schématu: SET search_path = '…'; 
    60                      * Poznámka: jeden „SET search_path TO "…"“ se obvykle předřazuje uživatelskému SQL (viz PiskovisteDAO.xml). 
    61                      */                     
    62                     while (ps.getMoreResults() || ps.getUpdateCount() > -1) { 
    63                         rs = ps.getResultSet(); 
    64                         if (rs == null) { 
    65                             /** Jedná se o „update count“. */ 
    66                         } else { 
    67                             v.getTabulky().add(zpracujVysledek(rs)); 
    68                         } 
    69                     } 
    70                     long dobaProvadeni = System.currentTimeMillis() - casPred; 
     81                                        if (isRS) { 
     82                                                rs = ps.getResultSet(); 
     83                                                v.getTabulky().add(zpracujVysledek(rs)); 
     84                                        } 
    7185 
    72                     /** Varování */ 
    73                     if (v.getHlasky().size() < 1 && v.getTabulky().size() < 1) { 
    74                         v.getHlasky().add(new Hlaska("SQL příkaz proběhl, ale nevrátil žádná data.", Typ.Varovani)); 
    75                     } 
     86                                        /** 
     87                                         * Ošetříme případ, kdy uživatel zadá SQL příkaz, který nevrací výsledkovou 
     88                                         * sadu. 
     89                                         * Typicky nastavení výchozího schématu: SET search_path = '…'; 
     90                                         * Poznámka: jeden „SET search_path TO "…"“ se obvykle předřazuje uživatelskému 
     91                                         * SQL (viz PiskovisteDAO.xml). 
     92                                         */ 
     93                                        while (ps.getMoreResults() || ps.getUpdateCount() > -1) { 
     94                                                rs = ps.getResultSet(); 
     95                                                if (rs == null) { 
     96                                                        /** Jedná se o „update count“. */ 
     97                                                } else { 
     98                                                        v.getTabulky().add(zpracujVysledek(rs)); 
     99                                                } 
     100                                        } 
     101                                        long dobaProvadeni = System.currentTimeMillis() - casPred; 
    76102 
    77                     /** Varování */ 
    78                     int pocitadloTabulek = 1; 
    79                     for (Tabulka t : v.getTabulky()) { 
    80                         if (t.getHodnoty().size() < 1) { 
    81                             v.getHlasky().add(new Hlaska("Tabulka " + pocitadloTabulek + "  je prázdná.", Typ.Varovani)); 
    82                         } 
    83                         pocitadloTabulek++; 
    84                     } 
     103                                        /** Varování */ 
     104                                        if (v.getHlasky().size() < 1 && v.getTabulky().size() < 1) { 
     105                                                v.getHlasky().add(new Hlaska("SQL příkaz proběhl, ale nevrátil žádná data.", Typ.Varovani)); 
     106                                        } 
    85107 
    86                     v.getHlasky().add(new Hlaska("SQL příkaz byl proveden úspěšně, během " + dobaProvadeni + " ms.", Typ.OK)); 
     108                                        /** Varování */ 
     109                                        int pocitadloTabulek = 1; 
     110                                        for (Tabulka t : v.getTabulky()) { 
     111                                                if (t.getHodnoty().size() < 1) { 
     112                                                        v.getHlasky().add(new Hlaska("Tabulka " + pocitadloTabulek + "  je prázdná.", Typ.Varovani)); 
     113                                                } 
     114                                                pocitadloTabulek++; 
     115                                        } 
    87116 
    88                 } catch (SQLException e) { 
    89                     log.log(Level.SEVERE, "SQL chyba při vykonávání uživatelského dotazu.", e); 
    90                     v.getHlasky().add(new Hlaska("Chybné SQL: " + e.getMessage(), Typ.Chyba)); 
    91                 } catch (Exception e) { 
    92                     log.log(Level.SEVERE, "Chyba při vykonávání uživatelského dotazu.", e); 
    93                     v.getHlasky().add(new Hlaska("Došlo k chybě dotazu.", Typ.Chyba)); 
    94                 } finally { 
    95                     zavri(db, ps, rs); 
    96                 } 
    97             } 
     117                                        v.getHlasky().add(new Hlaska("SQL příkaz byl proveden úspěšně, během " + dobaProvadeni + " ms.", Typ.OK)); 
    98118 
    99             /** Tip pro uživatele */ 
    100             String tip = tipy.getTip(); 
    101             if (tip != null) { 
    102                 v.getHlasky().add(new Hlaska(tip, Typ.Tip, false)); 
    103             } 
     119                                } catch (SQLException e) { 
     120                                        log.log(Level.SEVERE, "SQL chyba při vykonávání uživatelského dotazu.", e); 
     121                                        v.getHlasky().add(new Hlaska("Chybné SQL: " + e.getMessage(), Typ.Chyba)); 
     122                                } catch (Exception e) { 
     123                                        log.log(Level.SEVERE, "Chyba při vykonávání uživatelského dotazu.", e); 
     124                                        v.getHlasky().add(new Hlaska("Došlo k chybě dotazu.", Typ.Chyba)); 
     125                                } finally { 
     126                                        zavri(db, ps, rs); 
     127                                } 
     128                        } 
    104129 
    105         } else { 
    106             v.getHlasky().add(new Hlaska("Došlo k chybě historie.", Typ.Chyba)); 
    107         } 
    108         return v; 
    109     } 
     130                        /** Tip pro uživatele */ 
     131                        String tip = tipy.getTip(); 
     132                        if (tip != null) { 
     133                                v.getHlasky().add(new Hlaska(tip, Typ.Tip, false)); 
     134                        } 
    110135 
    111     private Tabulka zpracujVysledek(ResultSet rs) throws SQLException { 
    112         Tabulka t = new Tabulka(); 
     136                } else { 
     137                        v.getHlasky().add(new Hlaska("Došlo k chybě historie.", Typ.Chyba)); 
     138                } 
     139                return v; 
     140        } 
    113141 
    114         int pocetSloupecku = rs.getMetaData().getColumnCount(); 
    115         String[] zahlavi = new String[pocetSloupecku]; 
    116         t.setZahlavi(zahlavi); 
    117         for (int i = 0; i < pocetSloupecku; i++) { 
    118             zahlavi[i] = rs.getMetaData().getColumnName(i + 1); 
    119         } 
     142        private Tabulka zpracujVysledek(ResultSet rs) throws SQLException { 
     143                Tabulka t = new Tabulka(); 
    120144 
    121         while (rs.next()) { 
    122             Object[] hodnoty = new Object[pocetSloupecku]; 
    123             for (int i = 0; i < pocetSloupecku; i++) { 
    124                 hodnoty[i] = rs.getObject(i + 1); 
    125             } 
    126             t.getHodnoty().add(hodnoty); 
    127         } 
     145                int pocetSloupecku = rs.getMetaData().getColumnCount(); 
     146                String[] zahlavi = new String[pocetSloupecku]; 
     147                t.setZahlavi(zahlavi); 
     148                for (int i = 0; i < pocetSloupecku; i++) { 
     149                        zahlavi[i] = rs.getMetaData().getColumnName(i + 1); 
     150                } 
    128151 
    129         return t; 
    130     } 
     152                while (rs.next()) { 
     153                        Object[] hodnoty = new Object[pocetSloupecku]; 
     154                        for (int i = 0; i < pocetSloupecku; i++) { 
     155                                hodnoty[i] = rs.getObject(i + 1); 
     156                        } 
     157                        t.getHodnoty().add(hodnoty); 
     158                } 
     159 
     160                return t; 
     161        } 
    131162} 
  • java/sql-vyuka/src/java/cz/frantovo/sql/vyuka/dao/PiskovisteDAO.xml

    r18 r78  
    33<properties>     
    44    <!-- 
    5     PostgreSQL proměnná „search_path“ – nastavíme ji před každým uživatelským SQL dotazem, 
    6     aby se uživatelé vzájemně neovlivňovali. 
     5                PostgreSQL proměnná „search_path“ – nastavíme ji před každým uživatelským SQL dotazem, 
     6                aby se uživatelé vzájemně neovlivňovali. 
    77    --> 
    8     <entry key="VYCHOZI_CESTA"> 
     8        <entry key="VYCHOZI_CESTA"> 
    99        <![CDATA[ 
    1010        SET search_path TO "$user",public; 
    1111        ]]> 
    12     </entry> 
     12        </entry> 
     13        <!-- 
     14                Limit (vteřiny) pro vykonání jednoho SQL příkazu. 
     15                TODO: 
     16                        použít ps.setQueryTimeout(LIMIT_ČASU); 
     17                        až ho bude podporovat JDBC ovladač. 
     18        --> 
     19        <entry key="LIMIT_ČASU"> 
     20        <![CDATA[ 
     21        SET statement_timeout TO 3; 
     22        ]]> 
     23        </entry> 
    1324</properties>