zoukankan      html  css  js  c++  java
  • 网络安全系列 之 SQL注入学习总结

    1. sql注入概述

    程序里面如果使用了未经校验的外部输入来构造SQL语句,就很可能会引入SQL注入漏洞。

    注入攻击
    对于字符串输入,如果这个字符串将被解释为某种指令,那么需要特别注意防止注入攻击。sql注入、os命令注入、xml注入是典型的攻击类型。

    2. sql注入测试工具

    可以使用BurpSuite工具,浏览器上修改代理设置为burp工具配置的代理监听的IP端口。
    对浏览器发送的请求进行拦截并修改其中参数,尝试注入攻击。

    • sleep盲注: 返回时间>=5s,存在注入.
      {"stationCodes":"AD02C7CCAB6F4BF9A85BC010AF16AC62')xor(sleep(5))and('","ptIds":"","pmYearDate":"2018","page":1,"pageSize":10}
    • 普通注入or /and : id="1' or '1'='1" id="1' or '1'='2"
    • 报错注入: updatexml /extractvalue 、floor后group by。
    1. id=1 and updatexml(1,concat(0x7e,version(),0x7e),1) 返回version(),存在注入.
    2. id=1 and extractvalue(1,concat(0x7e,version()) 返回version(),存在注入.
    3. id=1 and (select 1 from (select count(),concat(version(),floor(rand(0)2))x from information_schema.tables group by x)a)
      返回version(),存在注入.

    3. sql注入防御方法

    3.1 问题来源

    1. 执行外部数拼接的SQL语句。
    2. 执行外部传入的整条SQL语句
    3. 在配置文件中的SQL语句没有使用预编译方式占位符。
    4. 校验函数有缺陷或占位符使用错误。

    3.2 防御方法

    1. 使用参数化语句
      sql语句预编译绑定变量,不直接拼接。

    2. 输入校验
      采用白名单或黑名单方式对入参进行检查。
      备注:ESAPI(Enterprise Security API)也提供了输入验证类 org.owasp.esapi.reference.DefaultValidator的实现。

    3. 输出编码
      在sql注入语境中,将发送给数据库的内容进行编码(或转义)是必需的操作,可以保证内容被正确的处理。
      针对Mysql的编码举例:在动态sql中提交的信息(LIKE语句中使用通配符的位置)添加引用。

        sql = sql.replace("%",“\%”);   //%匹配0个或多个任意字符
        sql = sql.replace("_",“\_”);    //_匹配任意一个字符
    

    备注:在动态sql和拼接sql场景下也可以利用ESAPI进行转义处理

        // ESAPI转义,防SQL注入
        public static String encodeForSql(String input) {
            MySQLCodec mysqlCodec = new MySQLCodec(MySQLCodec.Mode.STANDARD);
            return ESAPI.encoder().encodeForSQL(mysqlCodec, input);
        }
        // 说明: esapi需要有两个配置文件:ESAPI.properties、validation.properties
    

    小结:输入校验输出编码是处理注入问题(如xss)的一贯套路。

    4. SQL注入防御举例

    4.1 使用JDBC时,SQL语句进行了拼接

    1. 使用statement的executeQuery、execute、executeUpdate等函数时,传入的SQL语句拼接了来自外部的不可信参数。
    **错误示例**
        String userName = ctx.getAuthenticatedUserName(); //this is a constant
        //itemName是外部读入的参数拼接到SQL语句
        String sqlString = "SELECT * FROM t_item WHERE owner='" + userName + "' AND itemName='" + request.getParameter("itemName") + "'";
        stmt = connection.createStatement();
        rs = stmt.executeQuery(sqlString);
    

    解决方法 1) 使用预编译方式(不可信数据作为字段值); 2) 对拼接到SQL语句中的外部参数进行白名单校验(不可信数据作为表名,字段名,排序方式)。

    **正确示例**:使用白名单校验方式校验itemName
        String userName = ctx.getAuthenticatedUserName(); //this is a constant
        String itemName=getCleanedItemName(request.getParameter("itemName"));
        String sqlString = "SELECT * FROM t_item WHERE owner='" + userName + "' AND itemName='" + itemName + "'";
        stmt = connection.createStatement();
        rs = stmt.executeQuery(sqlString);
    
    2. 使用connection的PreparedStatement时,使用的SQL语句拼接了来自外部的不可信参数。
    **错误示例**
        String userName = ctx.getAuthenticatedUserName(); //this is a constant
        //itemName是外部读入的参数拼接到SQL语句
        String itemName = request.getParameter("itemName");
        // ...Ensure that the length of userName and itemName is legitimate
        // ...
        String sqlString = "SELECT * FROM t_item WHERE owner=? AND itemName='"+itemName+"'";
    
        PreparedStatement stmt = connection.prepareStatement(sqlString);
        stmt.setString(1, userName);
        rs = stmt.executeQuery();
    

    解决方法 1) 将拼接方式改为占位符方式; 2). 对拼接到SQL语句中的外部参数进行白名单校验。

    **正确示例**:所有的参数使用占位符
        String userName = ctx.getAuthenticatedUserName(); //this is a constant
        String itemName = request.getParameter("itemName");
        // ...Ensure that the length of userName and itemName is legitimate
        // ...
        String sqlString = "SELECT * FROM t_item WHERE owner=? AND itemName=?";
    
        PreparedStatement  stmt = connection.prepareStatement(sqlString);
        stmt.setString(1, userName); // jdbc编号从1开始
        stmt.setString(2, itemName);
        rs = stmt.executeQuery();
    
    3. 存储过程使用动态方式构建SQL语句,导致SQL注入风险。
    **错误示例**
    REATE PROCEDURE sp_queryItem
        @userName varchar(50),
        @itemName varchar(50) 
    AS 
    BEGIN 
        DECLARE @sql nvarchar(500); 
        SET @sql = 'SELECT * FROM t_item 
                    WHERE owner = ''' + @userName + '''
                    AND itemName = ''' + @itemName + '''';
        EXEC(@sql); 
    END 
    GO
    

    解决方法 采用参数化查询的方式

    **正确示例**:采用参数化查询的方式
    CREATE PROCEDURE sp_queryItem
        @userName varchar(50), 
        @itemName varchar(50) 
    AS 
    BEGIN 
        SELECT * FROM t_item  
        WHERE userName = @userName
        AND itemName = @itemName; 
    END 
    GO
    

    4.2 使用Hibernate时,调用API时,传入的SQL语句有拼接外部参数

    1. 调用createQuery时,传入的SQL语句拼接了来自外部的不可信参数。
    **错误示例**
        //SQL语句拼接不可信参数
        String itemName = request.getParameter("itemName");
        Query hqlQuery = session.createQuery("from Item as item where item.itemName = '" + itemName + "'");
        List<Item> hrs = (List<Item>) hqlQuery.list();
    

    解决方法 1) 对拼接到SQL语句中的外部参数进行白名单校验; 2) 使用hibernate的配置映射关系方式。

    **正确示例**:对外部参数进行白名单校验
        String itemName = request.getParameter("itemName");
        itemName=getCleanedItemName(itemName);//白名单校验
        Query hqlQuery = session.createQuery("from Item as item where item.itemName = '" + itemName + "'");
        List<Item> hrs = (List<Item>) hqlQuery.list();
    

    4.3 使用MyBatis时,SQL语句使用$占位符

    1. 配置文件使用$占位符
    错误示例:
        // 使用$,底层将使用简单拼接    
        <select id="getItems" resultClass="Item">
             SELECT * FROM t_item WHERE owner = $userName$ AND itemName = $itemName$
        </select>
    

    解决方法 1) 将$占位符改为#占位符; 2) 如果外部不可信数据作为表名,字段名,排序方式,则对外部参数进行白名单校验。

    **正确示例**:使用#占位符方式
        <select id="getItems" resultClass="Item">
             SELECT * FROM t_item WHERE owner = #userName# AND itemName =#itemName#
        </select>
    
    2. mybatis接口中的函数标签的SQL语句,使用了$占位符
    **错误示例**
        public interface IUserDAO { 
         //标注中的SQL语句通过$表示占位符,内部实现是单纯的拼接
        @Select("select *from User where id=${id}) 
            User getUser(@Param("id")String id);
        }
    
    **正确示例**:标注中的SQL语句通过'#'表示占位符,内部实现是参数化预处理
        public interface IUserDAO { 
        @Select("select *from User where id=#{id}) 
           User getUser(@Param("id")String id);
        }
    

    end.

    https://www.cnblogs.com/eaglediao/p/8547751.html

    2018.03.31

  • 相关阅读:
    LCA算法总结
    【福利】论机房如何关闭方正软件保护卡
    codevs 2190 有理逼近
    用C语言的rand()和srand()产生伪随机数的方法总结
    float,double等精度丢失问题 float,double内存表示
    #incldue<cctype>函数系列
    poj 2348 Euclid's Game 题解
    poj 2240 Arbitrage 题解
    洛谷 p1352 没有上司的舞会 题解
    BZOJ 1093 最大半连通子图 题解
  • 原文地址:https://www.cnblogs.com/eaglediao/p/8547751.html
Copyright © 2011-2022 走看看