網絡安全系列 之 SQL注入學習總結

1. sql注入概述

程序裏面若是使用了未經校驗的外部輸入來構造SQL語句,就極可能會引入SQL注入漏洞。java

注入攻擊
對於字符串輸入,若是這個字符串將被解釋爲某種指令,那麼須要特別注意防止注入攻擊。sql注入、os命令注入、xml注入是典型的攻擊類型。mysql

2. sql注入測試工具

能夠使用BurpSuite工具,瀏覽器上修改代理設置爲burp工具配置的代理監聽的IP端口。
對瀏覽器發送的請求進行攔截並修改其中參數,嘗試注入攻擊。git

  • 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語句預編譯綁定變量,不直接拼接。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)的一向套路。api

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語句中的外部參數進行白名單校驗。session

**正確示例**:全部的參數使用佔位符
    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

解決方法 採用參數化查詢的方式mybatis

**正確示例**:採用參數化查詢的方式
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.

http://www.javashuo.com/article/p-ykvyxuaf-ne.html

2018.03.31

相關文章
相關標籤/搜索