zoukankan      html  css  js  c++  java
  • ibatis Order By注入问题

    上周六单位被扫描出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) + " " + rs.getString(2) + " " + 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) + " " + rs.getString(2) + " "
    57.                     + rs.getInt(3));
    58.         }
    59.         con.close();
    60.     }
    61. }

    可以看到,在DAO层拦截之后,在daoProxy中已经过滤了注入攻击. 这样即可以保证安全,又可以让代码优雅.

  • 相关阅读:
    制作一个命令式的 React 弹出层组件 (适用 React Native)
    React 中的 onInput/onChange
    防抖和节流及对应的React Hooks封装
    React Native选择器组件-react-native-slidepicker
    React Portal
    Quartz学习 之 Jobs 和 Triggers
    Quartz学习 之 关键接口
    Quartz学习 之 入门
    JAVA NIO 原理探秘 --- Socket
    JAVA面试题
  • 原文地址:https://www.cnblogs.com/firstdream/p/8386122.html
Copyright © 2011-2022 走看看