ibatis Order By注入問題

壹頁書發表於2014-11-03
上週六單位被掃描出SQL隱碼攻擊漏洞
經過檢查,發現ibatis框架都可能出現這個問題.

如果有需求,讓你實現頁面grid所有欄位都能排序,你會怎麼做呢?
最簡單的做法就是從頁面把欄位名,排序型別傳回來,然後拼接在SQL裡面.(在使用EasyUI前端框架的時候,這樣做非常容易)
然後修改ibatis框架,將order by #排序欄位# #排序型別#改為 order by $排序欄位$ $排序型別$
實現所謂的動態查詢,就像下面的連結所寫的
http://blog.sina.com.cn/s/blog_4dacfb0101016y6b.html

實驗模擬這個過程,
create table t (id int primary key ,name varchar(20),grade int);
insert into t values(1,'edmond',1);
insert into t values(2,'edmond',2);
insert into t values(3,'edmond',1);
insert into t values(4,'edmond',3);
insert into t values(5,'edmond',1);
insert into t values(6,'edmond',5);

  1. public class Test {
  2.     private static String URL = "jdbc:mysql://127.0.0.1:3306/mvbox";
  3.     private static String USERNAME = "xx";
  4.     private static String PWD = "xx";

  5.     public static void main(String[] args) throws Exception {
  6.         //模擬從頁面傳輸過來的引數
  7.         String name = "edmond";
  8.         String sort = "grade";
  9.         String order = "desc";
  10.         dao(name, sort, order);
  11.     }

  12.     private static void dao(String name, String sort, String order) throws Exception {
  13.         Class.forName("com.mysql.jdbc.Driver");
  14.         Connection con = DriverManager.getConnection(URL, USERNAME, PWD);
  15.         PreparedStatement ps = con.prepareStatement("select id,name,grade from t where name=? order by " + sort + " " + order);
  16.         ps.setString(1, name);
  17.         ResultSet rs = ps.executeQuery();
  18.         while (rs.next()) {
  19.             System.out.println(rs.getInt(1) + "\t" + rs.getString(2) + "\t" + rs.getInt(3));
  20.         }
  21.         con.close();
  22.     }
  23. }
上面的程式碼模擬了ibatis使用$符號實現動態排序查詢的場景.執行結果如下

可以看到上述程式碼存在注入漏洞
如果對引數order注入如下內容,即可以作為暴力攻破帳號密碼的方式,又可以使用sleep掛起資料庫.
String order = "desc,(select if(substring(user(),1,2)='xx',sleep(4),-1))";

攻擊方式參考:
http://www.jxcm.net/shujuku/64.html

如何避免注入攻擊,並且用ibatis實現動態排序查詢呢?
我感覺可以使用受控注入的方式.(自己想的一個名詞)
在監聽器中獲取資料庫所有的列名稱,然後使用AOP攔截DAO層的方法,
將前臺傳入的引數,對比監聽器中獲取的資料庫列名稱,如果沒有任何匹配,則直接報錯,或者給一個預設的排序
ibatis的SQL還是使用$符號的方式.

模擬程式碼如下
  1. import java.sql.Connection;
  2. import java.sql.DriverManager;
  3. import java.sql.PreparedStatement;
  4. import java.sql.ResultSet;
  5. import java.sql.SQLException;
  6. import java.util.ArrayList;
  7. import java.util.List;

  8. public class Test {
  9.     private static String URL = "jdbc:mysql://127.0.0.1:3306/mvbox";
  10.     private static String USERNAME = "xx";
  11.     private static String PWD = "xx";

  12.     private static List<String> fieldList = new ArrayList<String>();

  13.     private static void getAllField() throws Exception {
  14.         Class.forName("com.mysql.jdbc.Driver");
  15.         Connection con = DriverManager.getConnection(URL, USERNAME, PWD);
  16.         PreparedStatement ps = con
  17.                 .prepareStatement("select column_name from information_schema.columns where table_schema not in ('information_schema','test','mysql','information_schema')");
  18.         ResultSet rs = ps.executeQuery();
  19.         while (rs.next()) {
  20.             fieldList.add(rs.getString(1));
  21.         }
  22.         rs.close();
  23.         ps.close();
  24.         con.close();
  25.     }

  26.     public static void main(String[] args) throws Exception {
  27.         // 模擬監聽器啟動
  28.         getAllField();
  29.         // 模擬從頁面傳輸過來的引數
  30.         String name = "edmond";
  31.         String sort = "grade";
  32.         String order ="desc,(select if(substring(user(),1,2)='xx',sleep(4),-1))";
  33.         daoProxy(name, sort, order);
  34.     }

  35.     private static void daoProxy(String name, String sort, String order)
  36.             throws Exception {
  37.         if (fieldList.contains(sort)
  38.                 && (order.toLowerCase().equals("desc") || order.toLowerCase()
  39.                         .equals("asc"))) {
  40.             dao(name, sort, order);
  41.         } else {
  42.             // 記錄日誌,進行錯誤處理
  43.             System.out.println("黑客,你媽媽喊你回家吃飯");
  44.         }
  45.     }

  46.     private static void dao(String name, String sort, String order)
  47.             throws Exception {
  48.         Class.forName("com.mysql.jdbc.Driver");
  49.         Connection con = DriverManager.getConnection(URL, USERNAME, PWD);
  50.         PreparedStatement ps = con
  51.                 .prepareStatement("select id,name,grade from t where name=? order by "
  52.                         + sort + " " + order);
  53.         ps.setString(1, name);
  54.         ResultSet rs = ps.executeQuery();
  55.         while (rs.next()) {
  56.             System.out.println(rs.getInt(1) + "\t" + rs.getString(2) + "\t"
  57.                     + rs.getInt(3));
  58.         }
  59.         con.close();
  60.     }
  61. }
可以看到,在DAO層攔截之後,在daoProxy中已經過濾了注入攻擊.
這樣即可以保證安全,又可以讓程式碼優雅.

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29254281/viewspace-1318239/,如需轉載,請註明出處,否則將追究法律責任。

相關文章