网络安全系列 之 SQL注入

Posted eaglediao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络安全系列 之 SQL注入相关的知识,希望对你有一定的参考价值。

1. sql注入概述

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

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

2. sql注入测试工具

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

3. sql注入防御方法

  • 问题主要来源:
  1. 执行外部数拼接的SQL语句。
  2. 执行外部传入的整条SQL语句
  3. 在配置文件中的SQL语句没有使用预编译方式占位符。
  4. 校验函数有缺陷或占位符使用错误。
  • 主要防御方法:
    1、 采用sql语句预编译和绑定变量
    2、 采用白名单和黑名单方式实现输入检查
    3、 在动态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

4. SQL注入防御举例

2.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
  • 解决方法
  1. 采用参数化查询的方式
正确示例:采用参数化查询的方式
CREATE PROCEDURE sp_queryItem
    @userName varchar(50), 
    @itemName varchar(50) 
AS 
BEGIN 
    SELECT * FROM t_item  
    WHERE userName = @userName
    AND itemName = @itemName; 
END 
GO

2.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();

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






以上是关于网络安全系列 之 SQL注入的主要内容,如果未能解决你的问题,请参考以下文章

安全测试 web安全测试 常规安全漏洞 可能存在SQL和JS注入漏洞场景分析。为什么自己没有找到漏洞,哪么可能存在漏洞场景是?SQL注入漏洞修复 JS注入漏洞修复 漏洞存在场景分析和修复示例(代码片段

《小迪网络安全笔记》 第十二节:WEB漏洞-SQL注入之简要SQL注入

网络安全之SQL注入深入分析

网络安全之 SQL 注入深入分析

以下代码片段是不是容易受到 Rails 5 中 SQL 注入的影响?

web安全之SQL注入