1-Web安全——初識SQL注入漏洞

目錄

 

1. 什麼是SQL注入

2. 爲什麼會有SQL注入

3. SQL注入的種類

4. SQL注入原理

5. 如何防止SQL注入


 

1. 什麼是SQL注入

SQL注入是web應用程序對用戶輸入的數據沒有嚴格進行合法校驗和過濾,導致前端傳入到後端的數據被攻擊者惡意修改,欺騙服務器執行SQL語句實現對數據庫的任意操作,這就是SQL注入漏洞的原理。

 

2. 爲什麼會有SQL注入

  1. 研發人員在開發過程中代碼邏輯不嚴謹造成的,例如沒有對SQL參數進行嚴格過濾
  2. 未啓用框架的安全配置,例如PHP的magic_quotes_gpc
  3. 未啓防火牆等其他安全防護設備

 

3. SQL注入的種類

SQL注入的種類主要有以下幾種:

  1. 聯合注入
  2. 布爾注入
  3. 延時注入
  4. 報錯注入
  5. insert注入

除了以上幾種,還有很多其他的SQL注入方式,後面會對這些注入方式進行詳細介紹。

 

4. SQL注入原理

現在通過一個用戶登錄的萬能密碼漏洞來分析SQL注入漏洞產生的原因,測試環境如下:

 

 

 

在實驗的測試環境中Post data一欄就是攻擊者惡意修改的數據,Load URL一欄就是進行SQL注入測試的網址,使用POST方式提交數據,點擊Execute提交測試的數據:

萬能密碼的測試結果中看到成功登陸了,還可以使用test1用戶進行發留言和刪除,退出等操作。但是現在我們對於萬能密碼漏洞產生的原因仍然無從得知,爲什麼攻擊者惡意僞造數據就能成功登陸?其實問題的根源就是代碼邏輯不嚴謹,缺乏安全意識導致的。

 

爲了進一步分析漏洞產生的原因,我們需要通過分析代碼來了解前後端的數據是如何交互的,以及後端和數據庫之前如何交互的。我們知道的是,點擊Execute提交數據時,測試數據就隨着測試的網址從前端傳入到後端。

 

 

分析登錄功能對應的源代碼:

 <?php
/**
 * Created by TEST  --  用戶登錄
 */

require "./lib/init.php";
header("Content-type:text/html;charset=utf-8");
if(empty($_POST))
{
    //header('Location: register.php');
    echo "登錄信息爲空";
    header("Refresh:3;url=login.php");
}else{
    //是否設置用戶名和密碼
    if(isset($_POST["user_name"]) && isset($_POST["user_pass"])){
        //去除空格字符
        $usename = trim($_POST["user_name"]);
        $password = trim($_POST["user_pass"]);
        //對密碼進行加密,然後拼接成SQL語句
        $password = md5($password);
        $sql = "select * from users where user_name='$usename' and user_pass='$password'";
        //var_dump($sql);
        //exit();
        //查詢數據庫
        $selectSQL = new MySql();
        $user_data = $selectSQL->getRow($sql);
        if($user_data!="")
        {
            session_start();
            $_SESSION["user"] = $user_data["user_name"];
            header("Location: user.php");
        }else{
            echo "用戶名或者密碼錯誤!";
            header("Refresh:3;url=login.php");
        }
    }else{
        echo "登錄信息不完整";
        header("Refresh:3;url=login.php");
    }
}

在以上的代碼中,對於前端傳入的用戶名和密碼等數據,後端只是簡單的去除空格字符並沒有進行嚴格的校驗和過濾,直接將用戶名和密碼拼接成SQL語句執行。

 

 

使用var_dump函數把拼接後的SQL語句打印出來:

 

最終拼接出來的SQL語句是這樣的:select * from users where user_name='123' or 1#' and user_pass='81dc9bdb52d04dc20036dbd8313ed055'  ,重要的是這條SQL語句在MYSQL數據庫中是一定能執行成功的

通過分析可知,前端傳入的用戶名和密碼中帶有“#”特殊字符,在MYSQL數據庫中“#”字符後面的內容會被當做註釋,但是後端代碼並沒有對“#”字符進行過濾而是直接拼接成SQL語句,也就是說,在這條SQL語句中“#”字符往後的全都當做註釋了,這就導致最終拼接的SQL語句實際上是這樣的:select * from users where user_name='123' or 1 。

 

MYSQL查詢到了所有的數據,卻只返回了第一條數據,在前端頁面爲啥是用的test1用戶登錄的呢?接着分析getRow函數的實現:

    //查詢單行
    public function getRow($sql){
        $res = $this->link->query($sql);
        //只截取第一條記錄
        $row = $res->fetch_assoc();
        return $row;
    }

數據庫查詢到的所有數據都放在res對象中,但fetch_assoc函數只從res對象中截取了第一條數據並返回結果,而第一條數據就是test1用戶,前端頁面登錄時用的恰好就是test1用戶。

 

 

5. 如何防止SQL注入

以上面的用戶登錄爲例,後端在接收前端傳入的數據時,例如一些特殊字符應該使用PHP的addslashes函數和mysqli_real_escape_string函數進行過濾。

 

對用戶登錄功能的代碼改進:

 

//去除空格字符
$usename = addslashes(trim($_POST["user_name"]));
$password = addslashes(trim($_POST["user_pass"]));

這兩個函數的作用就是對 SQL語句中的特殊字符進行轉義,當後端接收到前端傳入的數據時,addslashes函數會對特殊字符進行轉義,然後再進行測試:

 

 

由於addslashes函數會對數據中的特殊字符進行過濾轉義,此時登錄就會報錯,可以看到addslashes函數對用戶名中的“ ’”特殊字符使用斜槓字符進行轉義了:

 

這條SQL語句在MYSQL數據庫中無法查詢到數據的:

 

通過一個簡單的小案例我們基本瞭解了SQL注入的原理,產生SQL注入的原因以及如何防禦的技巧。前端傳入後端的參數可以由用戶控制,且參數拼接成SQL語句會被帶入數據庫中執行。在開發過程中只要滿足上述所說就有可能產生SQL注入漏洞,因此在開發過程中應秉承“外部參數皆不可信”的原則進行開發。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章